OLD | NEW |
---|---|
(Empty) | |
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 | |
5 #include "atrace_process_dump.h" | |
6 | |
7 #include <sys/time.h> | |
8 #include <sys/timerfd.h> | |
9 #include <stdint.h> | |
10 | |
11 #include <memory> | |
12 #include <sstream> | |
13 | |
14 #include "logging.h" | |
15 | |
16 void AtraceProcessDump::SetFullDumpConfig(const std::string& config) { | |
17 self_pid_ = (int) getpid(); | |
18 if (config == "all") { | |
19 full_dump_all_ = true; | |
20 } else if (config == "apps") { | |
21 full_dump_apps_ = true; | |
22 } else { | |
23 full_dump_whitelist_.clear(); | |
24 std::istringstream ss(config); | |
25 std::string entry; | |
26 while (std::getline(ss, entry, ',')) { | |
27 full_dump_whitelist_.insert(entry); | |
28 } | |
29 } | |
30 } | |
31 | |
32 void AtraceProcessDump::SerializeSnapshot( | |
33 const InstantProcessInfo* snapshot, const std::set<int>& smaps_pids) { | |
34 | |
35 fprintf(out_, "{\"ts\":\"%llu\",\"memdump\":{\n", snapshot->timestamp()); | |
36 const InstantProcessInfo::ProcessSnapshotMap* processes = | |
37 snapshot->processes(); | |
38 for (auto pit = processes->begin(); pit != processes->end();) { | |
39 const InstantProcessInfo::ProcessSnapshot* process = pit->second.get(); | |
40 const ProcessMemoryStats* mem = &process->memory; | |
41 fprintf(out_, "\"%d\":{", process->pid); | |
42 | |
43 fprintf(out_, "\"vm\":%llu,\"rss\":%llu", | |
44 mem->virt_kb(), mem->rss_kb()); | |
45 | |
46 fprintf(out_, ",\"oom_sc\":%d,\"oom_sc_adj\":%d" | |
47 ",\"min_flt\":%lu,\"maj_flt\":%lu" | |
48 ",\"utime\":%lu,\"stime\":%lu", | |
49 process->oom_score, process->oom_score_adj, | |
50 process->minor_faults, process->major_faults, | |
51 process->utime, process->stime); | |
52 | |
53 if (mem->full_stats_available()) { | |
54 fprintf(out_, ",\"pss\":%llu,\"swp\":%llu" | |
55 ",\"pc\":%llu,\"pd\":%llu,\"sc\":%llu,\"sd\":%llu", | |
56 mem->pss_kb(), mem->swapped_kb(), | |
57 mem->private_clean_kb(), mem->private_dirty_kb(), | |
58 mem->shared_clean_kb(), mem->shared_dirty_kb()); | |
59 } | |
60 | |
61 if (mem->gpu_stats_available()) { | |
62 fprintf(out_, ",\"gpu_egl\":%llu,\"gpu_egl_pss\":%llu" | |
63 ",\"gpu_gl\":%llu,\"gpu_gl_pss\":%llu" | |
64 ",\"gpu_etc\":%llu,\"gpu_etc_pss\":%llu", | |
65 mem->gpu_graphics_kb(), mem->gpu_graphics_pss_kb(), | |
66 mem->gpu_gl_kb(), mem->gpu_gl_pss_kb(), | |
67 mem->gpu_other_kb(), mem->gpu_other_pss_kb()); | |
68 } | |
69 | |
70 if (mem->full_stats_available() && smaps_pids.count(process->pid)) { | |
71 fprintf(out_, ", \"mmaps\":["); | |
72 int n_mmaps = mem->mmaps_count(); | |
Primiano Tucci (use gerrit)
2017/06/22 07:55:51
size_t, here and below
kraynov
2017/06/22 09:19:44
Done.
| |
73 for (int k = 0; k < n_mmaps; ++k) { | |
74 const ProcessMemoryStats::MmapInfo* mm = mem->mmap(k); | |
75 fprintf(out_, | |
76 "{\"vm\":\"%llx-%llx\",\"file\":\"%s\",\"flags\":\"%s\"," | |
77 "\"pss\":%llu,\"rss\":%llu,\"swp\":%llu," | |
78 "\"pc\":%llu,\"pd\":%llu," | |
79 "\"sc\":%llu,\"sd\":%llu}", | |
80 mm->start_addr, mm->end_addr, mm->mapped_file, mm->prot_flags, | |
81 mm->pss_kb, mm->rss_kb, mm->swapped_kb, | |
82 mm->private_clean_kb, mm->private_dirty_kb, | |
83 mm->shared_clean_kb, mm->shared_dirty_kb); | |
84 if (k < n_mmaps - 1) | |
85 fprintf(out_, ", "); | |
86 } | |
87 fprintf(out_, "]"); | |
88 } | |
89 | |
90 // TODO(kraynov): Return OOM numbers. | |
91 | |
92 ++pit; | |
93 if (pit != processes->end()) { | |
94 fprintf(out_, "},\n"); | |
95 } else { | |
96 fprintf(out_, "}}\n"); | |
97 } | |
98 } | |
99 fprintf(out_, "}"); | |
100 } | |
101 | |
102 void AtraceProcessDump::SerializeProcessInfo( | |
103 const PersistentProcessInfo::ProcessMap* processes) { | |
104 | |
105 fprintf(out_, "\"processes\":{"); | |
106 for (auto pit = processes->begin(); pit != processes->end();) { | |
107 const PersistentProcessInfo::ProcessInfo* process = pit->second.get(); | |
108 fprintf(out_, "\"%d\":{", process->pid); | |
109 fprintf(out_, "\"name\":\"%s\"", process->name.c_str()); | |
110 | |
111 if (!process->in_kernel) { | |
112 fprintf(out_, ",\"exe\":\"%s\",", process->exe.c_str()); | |
113 fprintf(out_, "\"threads\":{\n"); | |
114 const PersistentProcessInfo::ThreadMap* threads = &process->threads; | |
115 for (auto tit = threads->begin(); tit != threads->end();) { | |
116 const PersistentProcessInfo::ThreadInfo* thread = tit->second.get(); | |
117 fprintf(out_, "\"%d\":{", thread->tid); | |
118 fprintf(out_, "\"name\":\"%s\"", thread->name.c_str()); | |
119 | |
120 ++tit; | |
121 if (tit != threads->end()) { | |
122 fprintf(out_, "},\n"); | |
123 } else { | |
124 fprintf(out_, "}\n"); | |
125 } | |
126 } | |
127 fprintf(out_, "}"); | |
128 } | |
129 | |
130 ++pit; | |
131 if (pit != processes->end()) { | |
132 fprintf(out_, "},\n"); | |
133 } else { | |
134 fprintf(out_, "}\n"); | |
135 } | |
136 } | |
137 fprintf(out_, "}"); | |
138 } | |
139 | |
140 void AtraceProcessDump::RunAndPrintJson(FILE* stream) { | |
141 using ProcessInfo = PersistentProcessInfo::ProcessInfo; | |
142 out_ = stream; | |
143 | |
144 std::set<int> smaps_pids; | |
145 std::unique_ptr<ProcessDumpManager> mgr(new ProcessDumpManager()); | |
146 mgr->SetFullDumpPredicate([this](const ProcessInfo* process) -> bool { | |
147 if (full_dump_all_) | |
148 return !process->in_kernel && (process->pid != self_pid_); | |
149 if (full_dump_apps_) | |
150 return process->is_app; | |
151 if (full_dump_whitelist_.empty()) | |
152 return false; | |
153 return full_dump_whitelist_.count(process->name) > 0; | |
154 }); | |
155 mgr->SetGraphicsDumpPredicate([this](const ProcessInfo* process) -> bool { | |
156 if (!graphics_stats_) | |
157 return false; | |
158 return process->is_app; | |
159 }); | |
160 | |
161 SetupTimer(); | |
162 fprintf(out_, "{\"start_ts\": \"%llu\", \"snapshots\":[\n", GetTimestamp()); | |
163 for (int dump_number = 0; dump_number < dump_count_; dump_number++) { | |
164 if (dump_number > 0) { | |
165 fprintf(out_, ",\n"); | |
166 if (!WaitForTimer()) | |
167 break; // Interrupted by signal. | |
168 } | |
169 mgr->TakeSnapshot(); | |
170 | |
171 // Smaps are too heavy to serialize. Using only in whitelist mode. | |
172 if (print_smaps_ && !full_dump_whitelist_.empty()) { | |
173 smaps_pids.clear(); | |
174 auto processes = mgr->all_processes(); | |
175 for (auto pit = processes->begin(); pit != processes->end(); ++pit) { | |
176 if (full_dump_whitelist_.count(pit->second->name)) | |
177 smaps_pids.insert(pit->second->pid); | |
178 } | |
179 } | |
180 SerializeSnapshot(mgr->last_snapshot(), smaps_pids); | |
181 fflush(out_); | |
182 } | |
183 fprintf(out_, "],\n"); | |
184 SerializeProcessInfo(mgr->all_processes()); | |
185 CloseTimer(); | |
186 | |
187 fprintf(out_, "}\n"); | |
188 fclose(out_); | |
189 } | |
190 | |
191 void AtraceProcessDump::SetupTimer() { | |
192 timer_fd_ = timerfd_create(CLOCK_MONOTONIC, 0); | |
193 CHECK(timer_fd_ >= 0); | |
194 int sec = dump_interval_ms_ / 1000; | |
195 int nsec = (dump_interval_ms_ % 1000) * 1000000; | |
196 struct itimerspec ts = {}; | |
197 ts.it_value.tv_nsec = nsec; | |
198 ts.it_value.tv_sec = sec; | |
199 ts.it_interval.tv_nsec = nsec; | |
200 ts.it_interval.tv_sec = sec; | |
201 CHECK(timerfd_settime(timer_fd_, 0, &ts, nullptr) == 0); | |
202 } | |
203 | |
204 bool AtraceProcessDump::WaitForTimer() { | |
205 uint64_t stub = 0; | |
206 int res = read(timer_fd_, &stub, sizeof(stub)); | |
207 if (res < 0 && errno == EBADF) | |
208 return false; // Received SIGINT/SIGTERM signal. | |
209 CHECK(res > 0); | |
210 return true; | |
211 } | |
212 | |
213 void AtraceProcessDump::CloseTimer() { | |
214 if (timer_fd_ < 0) | |
215 return; | |
216 close(timer_fd_); | |
217 timer_fd_ = 0; | |
218 } | |
OLD | NEW |