main_utils.cc 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/zucchini/main_utils.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <ostream>
  8. #include <string>
  9. #include <vector>
  10. #include "base/command_line.h"
  11. #include "base/files/file_path.h"
  12. #include "base/files/file_util.h"
  13. #include "base/logging.h"
  14. #include "base/process/process_handle.h"
  15. #include "base/strings/string_number_conversions.h"
  16. #include "base/strings/string_split.h"
  17. #include "base/strings/string_util.h"
  18. #include "base/time/time.h"
  19. #include "build/build_config.h"
  20. #include "components/zucchini/io_utils.h"
  21. #include "components/zucchini/version_info.h"
  22. #include "components/zucchini/zucchini_commands.h"
  23. #if BUILDFLAG(IS_WIN)
  24. #include <windows.h> // This include must come first.
  25. #include <psapi.h>
  26. #endif
  27. namespace {
  28. /******** Command ********/
  29. // Specifications for a Zucchini command.
  30. struct Command {
  31. constexpr Command(const char* name_in,
  32. const char* usage_in,
  33. int num_args_in,
  34. CommandFunction command_function_in)
  35. : name(name_in),
  36. usage(usage_in),
  37. num_args(num_args_in),
  38. command_function(command_function_in) {}
  39. Command(const Command&) = default;
  40. ~Command() = default;
  41. // Unique name of command. |-name| is used to select from command-line.
  42. const char* const name;
  43. // Usage help text of command.
  44. const char* const usage;
  45. // Number of arguments (assumed to be filenames) used by the command.
  46. const int num_args;
  47. // Main function to run for the command.
  48. const CommandFunction command_function;
  49. };
  50. /******** List of Zucchini commands ********/
  51. constexpr Command kCommands[] = {
  52. {"gen",
  53. "-gen <old_file> <new_file> <patch_file> [-raw] [-keep]"
  54. " [-impose=#+#=#+#,#+#=#+#,...]",
  55. 3, &MainGen},
  56. {"apply", "-apply <old_file> <patch_file> <new_file> [-keep]", 3,
  57. &MainApply},
  58. {"verify", "-verify <patch_file>", 1, &MainVerify},
  59. {"read", "-read <exe> [-dump]", 1, &MainRead},
  60. {"detect", "-detect <archive_file>", 1, &MainDetect},
  61. {"match", "-match <old_file> <new_file> [-impose=#+#=#+#,#+#=#+#,...]", 2,
  62. &MainMatch},
  63. {"crc32", "-crc32 <file>", 1, &MainCrc32},
  64. };
  65. /******** GetPeakMemoryMetrics ********/
  66. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  67. // Linux does not have an exact mapping to the values used on Windows so use a
  68. // close approximation:
  69. // peak_virtual_memory ~= peak_page_file_usage
  70. // resident_set_size_hwm (high water mark) ~= peak_working_set_size
  71. //
  72. // On failure the input values will be set to 0.
  73. void GetPeakMemoryMetrics(size_t* peak_virtual_memory,
  74. size_t* resident_set_size_hwm) {
  75. *peak_virtual_memory = 0;
  76. *resident_set_size_hwm = 0;
  77. auto status_path =
  78. base::FilePath("/proc")
  79. .Append(base::NumberToString(base::GetCurrentProcessHandle()))
  80. .Append("status");
  81. std::string contents_string;
  82. base::ReadFileToString(status_path, &contents_string);
  83. std::vector<base::StringPiece> lines = base::SplitStringPiece(
  84. contents_string, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  85. for (const auto& line : lines) {
  86. // Tokens should generally be of the form "Metric: <val> kB"
  87. std::vector<base::StringPiece> tokens = base::SplitStringPiece(
  88. line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  89. if (tokens.size() < 2)
  90. continue;
  91. if (tokens[0] == "VmPeak:") {
  92. if (base::StringToSizeT(tokens[1], peak_virtual_memory)) {
  93. *peak_virtual_memory *= 1024; // in kiB
  94. if (*resident_set_size_hwm)
  95. return;
  96. }
  97. } else if (tokens[0] == "VmHWM:") {
  98. if (base::StringToSizeT(tokens[1], resident_set_size_hwm)) {
  99. *resident_set_size_hwm *= 1024; // in kiB
  100. if (*peak_virtual_memory)
  101. return;
  102. }
  103. }
  104. }
  105. }
  106. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  107. #if BUILDFLAG(IS_WIN)
  108. // On failure the input values will be set to 0.
  109. void GetPeakMemoryMetrics(size_t* peak_page_file_usage,
  110. size_t* peak_working_set_size) {
  111. *peak_page_file_usage = 0;
  112. *peak_working_set_size = 0;
  113. PROCESS_MEMORY_COUNTERS pmc;
  114. if (::GetProcessMemoryInfo(::GetCurrentProcess(), &pmc, sizeof(pmc))) {
  115. *peak_page_file_usage = pmc.PeakPagefileUsage;
  116. *peak_working_set_size = pmc.PeakWorkingSetSize;
  117. }
  118. }
  119. #endif // BUILDFLAG(IS_WIN)
  120. /******** ScopedResourceUsageTracker ********/
  121. // A class to track and log system resource usage.
  122. class ScopedResourceUsageTracker {
  123. public:
  124. // Initializes states for tracking.
  125. ScopedResourceUsageTracker() {
  126. start_time_ = base::TimeTicks::Now();
  127. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
  128. GetPeakMemoryMetrics(&start_peak_page_file_usage_,
  129. &start_peak_working_set_size_);
  130. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
  131. }
  132. // Computes and prints usage.
  133. ~ScopedResourceUsageTracker() {
  134. base::TimeTicks end_time = base::TimeTicks::Now();
  135. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
  136. size_t cur_peak_page_file_usage = 0;
  137. size_t cur_peak_working_set_size = 0;
  138. GetPeakMemoryMetrics(&cur_peak_page_file_usage, &cur_peak_working_set_size);
  139. LOG(INFO) << "Zucchini.PeakPagefileUsage "
  140. << cur_peak_page_file_usage / 1024 << " KiB";
  141. LOG(INFO) << "Zucchini.PeakPagefileUsageChange "
  142. << (cur_peak_page_file_usage - start_peak_page_file_usage_) / 1024
  143. << " KiB";
  144. LOG(INFO) << "Zucchini.PeakWorkingSetSize "
  145. << cur_peak_working_set_size / 1024 << " KiB";
  146. LOG(INFO) << "Zucchini.PeakWorkingSetSizeChange "
  147. << (cur_peak_working_set_size - start_peak_working_set_size_) /
  148. 1024
  149. << " KiB";
  150. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
  151. LOG(INFO) << "Zucchini.TotalTime " << (end_time - start_time_).InSecondsF()
  152. << " s";
  153. }
  154. private:
  155. base::TimeTicks start_time_;
  156. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
  157. size_t start_peak_page_file_usage_ = 0;
  158. size_t start_peak_working_set_size_ = 0;
  159. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
  160. };
  161. /******** Helper functions ********/
  162. // Translates |command_line| arguments to a vector of base::FilePath (expecting
  163. // exactly |expected_count|). On success, writes the results to |paths| and
  164. // returns true. Otherwise returns false.
  165. bool CheckAndGetFilePathParams(const base::CommandLine& command_line,
  166. size_t expected_count,
  167. std::vector<base::FilePath>* paths) {
  168. const base::CommandLine::StringVector& args = command_line.GetArgs();
  169. if (args.size() != expected_count)
  170. return false;
  171. paths->clear();
  172. paths->reserve(args.size());
  173. for (const auto& arg : args)
  174. paths->emplace_back(arg);
  175. return true;
  176. }
  177. // Prints main Zucchini usage text.
  178. void PrintUsage(std::ostream& err) {
  179. err << "Version: " << zucchini::kMajorVersion << "."
  180. << zucchini::kMinorVersion << std::endl;
  181. err << "Usage:" << std::endl;
  182. for (const Command& command : kCommands)
  183. err << " zucchini " << command.usage << std::endl;
  184. }
  185. } // namespace
  186. /******** Exported Functions ********/
  187. zucchini::status::Code RunZucchiniCommand(const base::CommandLine& command_line,
  188. std::ostream& out,
  189. std::ostream& err) {
  190. // Look for a command with name that matches input.
  191. const Command* command_use = nullptr;
  192. for (const Command& command : kCommands) {
  193. if (command_line.HasSwitch(command.name)) {
  194. if (command_use) { // Too many commands found.
  195. command_use = nullptr; // Set to null to flag error.
  196. break;
  197. }
  198. command_use = &command;
  199. }
  200. }
  201. // Expect exactly 1 matching command. If 0 or >= 2, print usage and quit.
  202. if (!command_use) {
  203. err << "Must have exactly one of:" << std::endl;
  204. err << " [";
  205. zucchini::PrefixSep sep(", ");
  206. for (const Command& command : kCommands)
  207. err << sep << "-" << command.name;
  208. err << "]" << std::endl;
  209. PrintUsage(err);
  210. return zucchini::status::kStatusInvalidParam;
  211. }
  212. // Try to parse filename arguments. On failure, print usage and quit.
  213. std::vector<base::FilePath> paths;
  214. if (!CheckAndGetFilePathParams(command_line, command_use->num_args, &paths)) {
  215. err << command_use->usage << std::endl;
  216. PrintUsage(err);
  217. return zucchini::status::kStatusInvalidParam;
  218. }
  219. ScopedResourceUsageTracker resource_usage_tracker;
  220. return command_use->command_function({command_line, paths, out, err});
  221. }