1 : // Copyright 2011 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include <iostream>
16 : #include <list>
17 :
18 : #include "base/at_exit.h"
19 : #include "base/command_line.h"
20 : #include "base/logging.h"
21 : #include "base/files/file_util.h"
22 : #include "base/process/process_iterator.h"
23 : #include "base/strings/utf_string_conversions.h"
24 : #include "pcrecpp.h" // NOLINT
25 : #include "syzygy/core/json_file_writer.h"
26 : #include "syzygy/wsdump/process_working_set.h"
27 :
28 m : using wsdump::ProcessWorkingSet;
29 :
30 m : namespace {
31 :
32 m : class RegexpProcessFilter: public base::ProcessFilter {
33 m : public:
34 m : RegexpProcessFilter();
35 :
36 m : bool Initialize(const std::string& regexpr);
37 :
38 m : virtual bool Includes(const base::ProcessEntry& entry) const override;
39 :
40 m : private:
41 m : pcrecpp::RE expr_;
42 m : };
43 :
44 m : RegexpProcessFilter::RegexpProcessFilter() : expr_("") {
45 m : }
46 :
47 m : bool RegexpProcessFilter::Initialize(const std::string& regexpr) {
48 m : pcrecpp::RE_Options options;
49 m : options.set_utf8(true);
50 m : options.set_caseless(true);
51 m : pcrecpp::RE new_expr(regexpr, options);
52 :
53 m : if (!new_expr.error().empty()) {
54 m : LOG(ERROR) << "Failed to initialize regular expression, error: "
55 m : << new_expr.error();
56 m : return false;
57 m : }
58 :
59 m : expr_ = new_expr;
60 m : DCHECK_EQ("", new_expr.error());
61 :
62 m : return true;
63 m : }
64 :
65 m : bool RegexpProcessFilter::Includes(const base::ProcessEntry& entry) const {
66 m : return expr_.PartialMatch(base::WideToUTF8(entry.exe_file()));
67 m : }
68 :
69 m : const char kUsage[] =
70 m : "Usage: wsdump [--process-name=<process_re>]\n"
71 m : "\n"
72 m : " Captures and outputs working set statistics for all processes,\n"
73 m : " or only for processess whose executable name matches <process_re>.\n"
74 m : "\n"
75 m : " The output is JSON encoded array, where each element of the array\n"
76 m : " is a dictionary describing a process. Each process has the following\n"
77 m : " items:\n"
78 m : " * exe_file - the process' executable file, e.g. \"chrome.exe\".\n"
79 m : " * pid - the process ID.\n"
80 m : " * parent_pid - the parent process ID.\n"
81 m : " * modules - an array of dictionaries, one for each module in the\n"
82 m : " process working set.\n"
83 m : " Each module has the following keys:\n"
84 m : " * module_name - the module file name, e.g. \"C:\\temp\\xyz.dll\"\n"
85 m : " * pages - total number of pages from this module in the working set.\n"
86 m : " * shareable_pages - shareable pages in the working set.\n"
87 m : " * shared_pages - shared pages in the working set.\n"
88 m : " * read_only_pages - read-only pages in the working set.\n"
89 m : " * writable_pages - writable pages in the working set.\n"
90 m : " * executable_pages - executable pages in the working set.\n"
91 m : "\n"
92 m : "Example Output:\n"
93 m : "[\n"
94 m : " {\n"
95 m : " \"exe_file\": \"devenv.exe\",\n"
96 m : " \"pid\": 5772,\n"
97 m : " \"parent_pid\": 3804,\n"
98 m : " \"modules\": [\n"
99 m : " {\n"
100 m : " \"module_name\": \"Total\",\n"
101 m : " \"pages\": 34145,\n"
102 m : " \"shareable_pages\": 10515,\n"
103 m : " \"shared_pages\": 4847,\n"
104 m : " \"read_only_pages\": 1951,\n"
105 m : " \"writable_pages\": 23235,\n"
106 m : " \"executable_pages\": 8959\n"
107 m : " },\n"
108 m : " {\n"
109 m : " ... \n";
110 :
111 m : int Usage() {
112 m : std::cout << kUsage;
113 m : return 1;
114 m : }
115 :
116 m : struct ProcessInfo {
117 m : ProcessInfo() : pid(0), parent_pid(0) {
118 m : }
119 :
120 m : std::wstring exe_file;
121 m : base::ProcessId pid;
122 m : base::ProcessId parent_pid;
123 m : ProcessWorkingSet ws;
124 m : };
125 :
126 m : void OutputModule(const std::wstring& module_name,
127 m : const ProcessWorkingSet::Stats& stats,
128 m : core::JSONFileWriter* json) {
129 m : DCHECK(json != NULL);
130 :
131 m : json->OpenDict();
132 m : json->OutputKey("module_name");
133 m : json->OutputString(module_name);
134 m : json->OutputKey("pages");
135 m : json->OutputInteger(stats.pages);
136 m : json->OutputKey("shareable_pages");
137 m : json->OutputInteger(stats.shareable_pages);
138 m : json->OutputKey("shared_pages");
139 m : json->OutputInteger(stats.shared_pages);
140 m : json->OutputKey("read_only_pages");
141 m : json->OutputInteger(stats.read_only_pages);
142 m : json->OutputKey("writable_pages");
143 m : json->OutputInteger(stats.writable_pages);
144 m : json->OutputKey("executable_pages");
145 m : json->OutputInteger(stats.executable_pages);
146 m : json->CloseDict();
147 m : }
148 :
149 m : void OutputProcessInfo(const ProcessInfo& info,
150 m : core::JSONFileWriter* json) {
151 m : DCHECK(json != NULL);
152 :
153 m : json->OpenDict();
154 m : json->OutputKey("exe_file");
155 m : json->OutputString(info.exe_file);
156 m : json->OutputKey("pid");
157 m : json->OutputInteger(info.pid);
158 m : json->OutputKey("parent_pid");
159 m : json->OutputInteger(info.parent_pid);
160 :
161 m : json->OutputKey("modules");
162 m : json->OpenList();
163 m : OutputModule(L"Total", info.ws.total_stats(), json);
164 :
165 m : ProcessWorkingSet::ModuleStatsVector::const_iterator it =
166 m : info.ws.module_stats().begin();
167 m : ProcessWorkingSet::ModuleStatsVector::const_iterator end =
168 m : info.ws.module_stats().end();
169 m : for (; it != end; ++it)
170 m : OutputModule(it->module_name, *it, json);
171 :
172 m : json->CloseList();
173 m : json->CloseDict();
174 m : }
175 :
176 m : } // namespace
177 :
178 m : int main(int argc, char** argv) {
179 m : base::AtExitManager at_exit_manager;
180 m : base::CommandLine::Init(argc, argv);
181 :
182 m : logging::LoggingSettings settings;
183 m : settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
184 m : settings.lock_log = logging::DONT_LOCK_LOG_FILE;
185 m : settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
186 m : if (!logging::InitLogging(settings))
187 m : return 1;
188 :
189 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
190 m : DCHECK(cmd_line != NULL);
191 :
192 m : if (cmd_line->HasSwitch("help") || !cmd_line->GetArgs().empty()) {
193 m : return Usage();
194 m : }
195 :
196 : // If the process-name is empty or missing we match all processes.
197 m : std::string process_re = cmd_line->GetSwitchValueASCII("process-name");
198 m : RegexpProcessFilter filter;
199 m : if (!filter.Initialize(process_re)) {
200 m : LOG(ERROR) << "Incorrect process filter regular expression.";
201 m : return 1;
202 m : }
203 :
204 m : typedef std::list<ProcessInfo> WorkingSets;
205 m : WorkingSets working_sets;
206 :
207 m : const base::ProcessEntry* entry = NULL;
208 m : base::ProcessIterator process_iterator(&filter);
209 m : entry = process_iterator.NextProcessEntry();
210 m : while (entry) {
211 m : working_sets.push_back(ProcessInfo());
212 m : ProcessInfo& info = working_sets.back();
213 m : if (info.ws.Initialize(entry->pid())) {
214 m : info.exe_file = entry->exe_file();
215 m : info.pid = entry->pid();
216 m : info.parent_pid = entry->parent_pid();
217 m : } else {
218 m : LOG(ERROR) << "Unable to capture working set information for pid: "
219 m : << entry->pid();
220 m : working_sets.pop_back();
221 m : }
222 m : entry = process_iterator.NextProcessEntry();
223 m : }
224 :
225 m : core::JSONFileWriter json(stdout, true);
226 m : json.OpenList();
227 m : WorkingSets::const_iterator it = working_sets.begin();
228 m : for (; it != working_sets.end(); ++it) {
229 m : WorkingSets::const_iterator next = it;
230 m : ++next;
231 m : OutputProcessInfo(*it, &json);
232 m : }
233 m : json.CloseList();
234 m : json.Flush();
235 :
236 m : return 0;
237 m : }
|