OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <dirent.h> | |
6 #include <signal.h> | 5 #include <signal.h> |
7 #include <stdio.h> | 6 #include <stdio.h> |
8 #include <stdlib.h> | 7 #include <stdlib.h> |
9 #include <string.h> | 8 #include <string.h> |
10 #include <sys/time.h> | |
11 #include <sys/timerfd.h> | |
12 #include <sys/types.h> | |
13 | 9 |
14 #include <limits> | 10 #include <limits> |
15 #include <memory> | 11 #include <memory> |
| 12 #include <set> |
| 13 #include <string> |
| 14 #include <sstream> |
16 | 15 |
17 #include "file_utils.h" | 16 #include "atrace_process_dump.h" |
18 #include "logging.h" | 17 #include "logging.h" |
19 #include "process_info.h" | |
20 | 18 |
21 namespace { | 19 namespace { |
22 | 20 |
23 using ProcessMap = std::map<int, std::unique_ptr<ProcessInfo>>; | 21 std::unique_ptr<AtraceProcessDump> g_prog; |
24 | 22 |
25 int g_timer; | 23 void ParseFullDumpConfig(const std::string& config, AtraceProcessDump* prog) { |
26 | 24 using FullDumpMode = AtraceProcessDump::FullDumpMode; |
27 std::unique_ptr<ProcessMap> CollectStatsForAllProcs(bool full_mem_stats, | 25 if (config == "all") { |
28 bool gpu_mem_stats) { | 26 prog->set_full_dump_mode(FullDumpMode::kAllProcesses); |
29 std::unique_ptr<ProcessMap> procs(new ProcessMap()); | 27 } else if (config == "apps") { |
30 file_utils::ForEachPidInProcPath("/proc", | 28 prog->set_full_dump_mode(FullDumpMode::kAllJavaApps); |
31 [&procs, full_mem_stats, gpu_mem_stats](int pid) { | 29 } else { |
32 | 30 std::set<std::string> whitelist; |
33 if (!ProcessInfo::IsProcess(pid)) | 31 std::istringstream ss(config); |
| 32 std::string entry; |
| 33 while (std::getline(ss, entry, ',')) { |
| 34 whitelist.insert(entry); |
| 35 } |
| 36 if (whitelist.empty()) |
34 return; | 37 return; |
35 CHECK(procs->count(pid) == 0); | 38 prog->set_full_dump_mode(FullDumpMode::kOnlyWhitelisted); |
36 std::unique_ptr<ProcessInfo> pinfo(new ProcessInfo(pid)); | 39 prog->set_full_dump_whitelist(whitelist); |
37 if (!(pinfo->ReadProcessName() && pinfo->ReadThreadNames() && | |
38 pinfo->ReadOOMStats() && pinfo->ReadPageFaultsAndCPUTimeStats())) | |
39 return; | |
40 | |
41 if (full_mem_stats) { | |
42 if (!pinfo->memory()->ReadFullStats()) | |
43 return; | |
44 } else { | |
45 if (!pinfo->memory()->ReadLightStats()) | |
46 return; | |
47 } | |
48 if (gpu_mem_stats) { | |
49 // It might fail on some devices. | |
50 pinfo->memory()->ReadMemtrackStats(); | |
51 } | |
52 (*procs)[pid] = std::move(pinfo); | |
53 }); | |
54 return procs; | |
55 } | |
56 | |
57 void SerializeSnapshot(const ProcessMap& procs, | |
58 FILE* stream, | |
59 bool full_mem_stats, | |
60 bool gpu_mem_stats) { | |
61 struct timespec ts = {}; | |
62 CHECK(clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0); | |
63 fprintf(stream, "{\n"); | |
64 fprintf(stream, " \"ts\": %lu,\n", | |
65 (ts.tv_sec * 1000 + ts.tv_nsec / 1000000ul)); | |
66 fprintf(stream, " \"processes\": [\n"); | |
67 for (auto it = procs.begin(); it != procs.end();) { | |
68 int pid = it->first; | |
69 const ProcessInfo& pinfo = *it->second; | |
70 fprintf(stream, " {\"pid\": %d, \"name\": \"%s\", \"exe\": \"%s\"", pid, | |
71 pinfo.name(), pinfo.exe()); | |
72 fprintf(stream, ", \"threads\": ["); | |
73 for (auto t = pinfo.threads()->begin(); t != pinfo.threads()->end();) { | |
74 fprintf(stream, "{\"tid\": %d, \"name\":\"%s\"", t->first, | |
75 t->second->name); | |
76 t++; | |
77 fprintf(stream, t != pinfo.threads()->end() ? "}, " : "}"); | |
78 } | |
79 fprintf(stream, "]"); | |
80 | |
81 const ProcessMemoryStats* mem_info = pinfo.memory(); | |
82 fprintf(stream, ", \"mem\": {\"vm\": %llu, \"rss\": %llu", | |
83 mem_info->virt_kb(), mem_info->rss_kb()); | |
84 if (full_mem_stats) { | |
85 fprintf(stream, | |
86 ", \"pss\": %llu, \"swp\": %llu, \"pc\": %llu, \"pd\": %llu, " | |
87 "\"sc\": %llu, \"sd\": %llu", | |
88 mem_info->pss_kb(), mem_info->swapped_kb(), | |
89 mem_info->private_clean_kb(), mem_info->private_dirty_kb(), | |
90 mem_info->shared_clean_kb(), mem_info->shared_dirty_kb()); | |
91 } | |
92 if (gpu_mem_stats) { | |
93 fprintf(stream, | |
94 ", \"gpu\": %llu, \"gpu_pss\": %llu" | |
95 ", \"gpu_gl\": %llu, \"gpu_gl_pss\": %llu" | |
96 ", \"gpu_etc\": %llu, \"gpu_etc_pss\": %llu", | |
97 mem_info->gpu_graphics_kb(), mem_info->gpu_graphics_pss_kb(), | |
98 mem_info->gpu_gl_kb(), mem_info->gpu_gl_pss_kb(), | |
99 mem_info->gpu_other_kb(), mem_info->gpu_other_pss_kb()); | |
100 } | |
101 fprintf(stream, "}"); | |
102 | |
103 fprintf(stream, | |
104 ", \"oom\": {\"adj\": %d, \"score_adj\": %d, \"score\": %d}", | |
105 pinfo.oom_adj(), pinfo.oom_score_adj(), pinfo.oom_score()); | |
106 fprintf(stream, | |
107 ", \"stat\": {\"minflt\": %lu, \"majflt\": %lu, " | |
108 "\"utime\": %lu, \"stime\": %lu }", | |
109 pinfo.minflt(), pinfo.majflt(), pinfo.utime(), pinfo.stime()); | |
110 fprintf(stream, "}"); | |
111 it++; | |
112 fprintf(stream, it != procs.end() ? ",\n" : "\n"); | |
113 } | 40 } |
114 fprintf(stream, " ]\n"); | |
115 fprintf(stream, "}\n"); | |
116 } | 41 } |
117 | 42 |
118 } // namespace | 43 } // namespace |
119 | 44 |
120 int main(int argc, char** argv) { | 45 int main(int argc, char** argv) { |
121 bool background = false; | 46 bool background = false; |
122 int dump_interval_ms = 5000; | 47 int dump_interval_ms = 5000; |
123 char out_file[PATH_MAX] = {}; | 48 char out_file[PATH_MAX] = {}; |
124 bool dump_to_file = false; | 49 bool dump_to_file = false; |
125 bool full_mem_stats = false; | |
126 bool gpu_mem_stats = false; | |
127 int count = std::numeric_limits<int>::max(); | 50 int count = std::numeric_limits<int>::max(); |
| 51 |
| 52 AtraceProcessDump* prog = new AtraceProcessDump(); |
| 53 g_prog = std::unique_ptr<AtraceProcessDump>(prog); |
| 54 |
| 55 if (geteuid()) { |
| 56 fprintf(stderr, "Must run as root\n"); |
| 57 exit(EXIT_FAILURE); |
| 58 } |
| 59 |
128 int opt; | 60 int opt; |
129 while ((opt = getopt(argc, argv, "bmgt:o:c:")) != -1) { | 61 while ((opt = getopt(argc, argv, "bm:gst:o:c:")) != -1) { |
130 switch (opt) { | 62 switch (opt) { |
131 case 'b': | 63 case 'b': |
132 background = true; | 64 background = true; |
133 break; | 65 break; |
134 case 'm': | 66 case 'm': |
135 full_mem_stats = true; | 67 ParseFullDumpConfig(optarg, prog); |
136 break; | 68 break; |
137 case 'g': | 69 case 'g': |
138 gpu_mem_stats = true; | 70 prog->enable_graphics_stats(); |
| 71 break; |
| 72 case 's': |
| 73 prog->enable_print_smaps(); |
139 break; | 74 break; |
140 case 't': | 75 case 't': |
141 dump_interval_ms = atoi(optarg); | 76 dump_interval_ms = atoi(optarg); |
142 CHECK(dump_interval_ms > 0); | 77 CHECK(dump_interval_ms > 0); |
143 break; | 78 break; |
144 case 'c': | 79 case 'c': |
145 count = atoi(optarg); | 80 count = atoi(optarg); |
146 CHECK(count > 0); | 81 CHECK(count > 0); |
147 break; | 82 break; |
148 case 'o': | 83 case 'o': |
149 strncpy(out_file, optarg, sizeof(out_file)); | 84 strncpy(out_file, optarg, sizeof(out_file)); |
150 dump_to_file = true; | 85 dump_to_file = true; |
151 break; | 86 break; |
152 default: | 87 default: |
153 fprintf(stderr, | 88 fprintf(stderr, |
154 "Usage: %s [-b] [-m] [-g] [-t dump_interval_ms] " | 89 "Usage: %s [-b] [-m full_dump_filter] [-g] [-s] " |
| 90 "[-t dump_interval_ms] " |
155 "[-c dumps_count] [-o out.json]\n", | 91 "[-c dumps_count] [-o out.json]\n", |
156 argv[0]); | 92 argv[0]); |
157 exit(EXIT_FAILURE); | 93 exit(EXIT_FAILURE); |
158 } | 94 } |
159 } | 95 } |
160 | 96 |
161 if (geteuid()) { | 97 prog->set_dump_count(count); |
162 fprintf(stderr, "Must run as root\n"); | 98 prog->set_dump_interval(dump_interval_ms); |
163 exit(EXIT_FAILURE); | |
164 } | |
165 | 99 |
166 FILE* out_stream = stdout; | 100 FILE* out_stream = stdout; |
167 char tmp_file[PATH_MAX]; | 101 char tmp_file[PATH_MAX]; |
168 if (dump_to_file) { | 102 if (dump_to_file) { |
169 unlink(out_file); | 103 unlink(out_file); |
170 sprintf(tmp_file, "%s.tmp", out_file); | 104 sprintf(tmp_file, "%s.tmp", out_file); |
171 out_stream = fopen(tmp_file, "w"); | 105 out_stream = fopen(tmp_file, "w"); |
172 CHECK(out_stream); | 106 CHECK(out_stream); |
173 } | 107 } |
174 | 108 |
175 if (background) { | 109 if (background) { |
176 if (!dump_to_file) { | 110 if (!dump_to_file) { |
177 fprintf(stderr, "-b requires -o for output dump path\n"); | 111 fprintf(stderr, "-b requires -o for output dump path.\n"); |
178 exit(EXIT_FAILURE); | 112 exit(EXIT_FAILURE); |
179 } | 113 } |
180 printf("Continuing in background. kill -TERM to terminate the daemon.\n"); | 114 printf("Continuing in background. kill -TERM to terminate the daemon.\n"); |
181 CHECK(daemon(0 /* nochdir */, 0 /* noclose */) == 0); | 115 CHECK(daemon(0 /* nochdir */, 0 /* noclose */) == 0); |
182 } | 116 } |
183 | 117 |
184 g_timer = timerfd_create(CLOCK_MONOTONIC, 0); | 118 auto on_exit = [](int) { g_prog->Stop(); }; |
185 CHECK(g_timer >= 0); | |
186 struct itimerspec ts = {}; | |
187 ts.it_value.tv_nsec = 1; // Get the first snapshot immediately. | |
188 ts.it_interval.tv_nsec = (dump_interval_ms % 1000) * 1000000ul; | |
189 ts.it_interval.tv_sec = dump_interval_ms / 1000; | |
190 CHECK(timerfd_settime(g_timer, 0, &ts, nullptr) == 0); | |
191 | |
192 // Closing the g_timer fd on SIGINT/SIGTERM will cause the read() below to | |
193 // unblock and fail with EBADF, hence allowing the loop below to finalize | |
194 // the file and exit. | |
195 auto on_exit = [](int) { close(g_timer); }; | |
196 signal(SIGINT, on_exit); | 119 signal(SIGINT, on_exit); |
197 signal(SIGTERM, on_exit); | 120 signal(SIGTERM, on_exit); |
198 | 121 |
199 fprintf(out_stream, "{\"snapshots\": [\n"); | 122 prog->RunAndPrintJson(out_stream); |
200 bool is_first_snapshot = true; | 123 fclose(out_stream); |
201 for (; count > 0; count--) { | |
202 uint64_t missed = 0; | |
203 int res = read(g_timer, &missed, sizeof(missed)); | |
204 if (res < 0 && errno == EBADF) | |
205 break; // Received SIGINT/SIGTERM signal. | |
206 CHECK(res > 0); | |
207 if (!is_first_snapshot) | |
208 fprintf(out_stream, ","); | |
209 is_first_snapshot = false; | |
210 | 124 |
211 std::unique_ptr<ProcessMap> procs = | |
212 CollectStatsForAllProcs(full_mem_stats, gpu_mem_stats); | |
213 SerializeSnapshot(*procs, out_stream, full_mem_stats, gpu_mem_stats); | |
214 fflush(out_stream); | |
215 } | |
216 fprintf(out_stream, "]}\n"); | |
217 fclose(out_stream); | |
218 if (dump_to_file) | 125 if (dump_to_file) |
219 rename(tmp_file, out_file); | 126 rename(tmp_file, out_file); |
220 } | 127 } |
OLD | NEW |