1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "event_selection_set.h"
18
19 #include <algorithm>
20 #include <atomic>
21 #include <thread>
22
23 #include <android-base/logging.h>
24
25 #include "environment.h"
26 #include "event_attr.h"
27 #include "event_type.h"
28 #include "IOEventLoop.h"
29 #include "perf_regs.h"
30 #include "utils.h"
31 #include "RecordReadThread.h"
32
IsBranchSamplingSupported()33 bool IsBranchSamplingSupported() {
34 const EventType* type = FindEventTypeByName("cpu-cycles");
35 if (type == nullptr) {
36 return false;
37 }
38 perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
39 attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
40 attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
41 return IsEventAttrSupported(attr);
42 }
43
IsDwarfCallChainSamplingSupported()44 bool IsDwarfCallChainSamplingSupported() {
45 const EventType* type = FindEventTypeByName("cpu-cycles");
46 if (type == nullptr) {
47 return false;
48 }
49 perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
50 attr.sample_type |=
51 PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER;
52 attr.exclude_callchain_user = 1;
53 attr.sample_regs_user = GetSupportedRegMask(GetBuildArch());
54 attr.sample_stack_user = 8192;
55 return IsEventAttrSupported(attr);
56 }
57
IsDumpingRegsForTracepointEventsSupported()58 bool IsDumpingRegsForTracepointEventsSupported() {
59 const EventType* event_type = FindEventTypeByName("sched:sched_switch", false);
60 if (event_type == nullptr) {
61 return false;
62 }
63 std::atomic<bool> done(false);
64 std::atomic<pid_t> thread_id(0);
65 std::thread thread([&]() {
66 thread_id = gettid();
67 while (!done) {
68 usleep(1);
69 }
70 usleep(1); // Make a sched out to generate one sample.
71 });
72 while (thread_id == 0) {
73 usleep(1);
74 }
75 perf_event_attr attr = CreateDefaultPerfEventAttr(*event_type);
76 attr.freq = 0;
77 attr.sample_period = 1;
78 std::unique_ptr<EventFd> event_fd =
79 EventFd::OpenEventFile(attr, thread_id, -1, nullptr);
80 if (event_fd == nullptr) {
81 return false;
82 }
83 if (!event_fd->CreateMappedBuffer(4, true)) {
84 return false;
85 }
86 done = true;
87 thread.join();
88
89 std::vector<char> buffer = event_fd->GetAvailableMmapData();
90 std::vector<std::unique_ptr<Record>> records =
91 ReadRecordsFromBuffer(attr, buffer.data(), buffer.size());
92 for (auto& r : records) {
93 if (r->type() == PERF_RECORD_SAMPLE) {
94 auto& record = *static_cast<SampleRecord*>(r.get());
95 if (record.ip_data.ip != 0) {
96 return true;
97 }
98 }
99 }
100 return false;
101 }
102
IsSettingClockIdSupported()103 bool IsSettingClockIdSupported() {
104 // Do the real check only once and keep the result in a static variable.
105 static int is_supported = -1;
106 if (is_supported == -1) {
107 const EventType* type = FindEventTypeByName("cpu-cycles");
108 if (type == nullptr) {
109 is_supported = 0;
110 } else {
111 // Check if the kernel supports setting clockid, which was added in kernel 4.0. Just check
112 // with one clockid is enough. Because all needed clockids were supported before kernel 4.0.
113 perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
114 attr.use_clockid = 1;
115 attr.clockid = CLOCK_MONOTONIC;
116 is_supported = IsEventAttrSupported(attr) ? 1 : 0;
117 }
118 }
119 return is_supported;
120 }
121
IsMmap2Supported()122 bool IsMmap2Supported() {
123 const EventType* type = FindEventTypeByName("cpu-cycles");
124 if (type == nullptr) {
125 return false;
126 }
127 perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
128 attr.mmap2 = 1;
129 return IsEventAttrSupported(attr);
130 }
131
EventSelectionSet(bool for_stat_cmd)132 EventSelectionSet::EventSelectionSet(bool for_stat_cmd)
133 : for_stat_cmd_(for_stat_cmd), loop_(new IOEventLoop) {}
134
~EventSelectionSet()135 EventSelectionSet::~EventSelectionSet() {}
136
BuildAndCheckEventSelection(const std::string & event_name,bool first_event,EventSelection * selection)137 bool EventSelectionSet::BuildAndCheckEventSelection(const std::string& event_name, bool first_event,
138 EventSelection* selection) {
139 std::unique_ptr<EventTypeAndModifier> event_type = ParseEventType(event_name);
140 if (event_type == nullptr) {
141 return false;
142 }
143 if (for_stat_cmd_) {
144 if (event_type->event_type.name == "cpu-clock" ||
145 event_type->event_type.name == "task-clock") {
146 if (event_type->exclude_user || event_type->exclude_kernel) {
147 LOG(ERROR) << "Modifier u and modifier k used in event type "
148 << event_type->event_type.name
149 << " are not supported by the kernel.";
150 return false;
151 }
152 }
153 }
154 selection->event_type_modifier = *event_type;
155 selection->event_attr = CreateDefaultPerfEventAttr(event_type->event_type);
156 selection->event_attr.exclude_user = event_type->exclude_user;
157 selection->event_attr.exclude_kernel = event_type->exclude_kernel;
158 selection->event_attr.exclude_hv = event_type->exclude_hv;
159 selection->event_attr.exclude_host = event_type->exclude_host;
160 selection->event_attr.exclude_guest = event_type->exclude_guest;
161 selection->event_attr.precise_ip = event_type->precise_ip;
162 bool set_default_sample_freq = false;
163 if (!for_stat_cmd_) {
164 if (event_type->event_type.type == PERF_TYPE_TRACEPOINT) {
165 selection->event_attr.freq = 0;
166 selection->event_attr.sample_period = DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT;
167 } else {
168 selection->event_attr.freq = 1;
169 // Set default sample freq here may print msg "Adjust sample freq to max allowed sample
170 // freq". But this is misleading. Because default sample freq may not be the final sample
171 // freq we use. So use minimum sample freq (1) here.
172 selection->event_attr.sample_freq = 1;
173 set_default_sample_freq = true;
174 }
175 // We only need to dump mmap and comm records for the first event type. Because all event types
176 // are monitoring the same processes.
177 if (first_event) {
178 selection->event_attr.mmap = 1;
179 selection->event_attr.comm = 1;
180 if (IsMmap2Supported()) {
181 selection->event_attr.mmap2 = 1;
182 }
183 }
184 }
185 if (!IsEventAttrSupported(selection->event_attr)) {
186 LOG(ERROR) << "Event type '" << event_type->name
187 << "' is not supported on the device";
188 return false;
189 }
190 if (set_default_sample_freq) {
191 selection->event_attr.sample_freq = DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT;
192 }
193
194 selection->event_fds.clear();
195
196 for (const auto& group : groups_) {
197 for (const auto& sel : group) {
198 if (sel.event_type_modifier.name == selection->event_type_modifier.name) {
199 LOG(ERROR) << "Event type '" << sel.event_type_modifier.name
200 << "' appears more than once";
201 return false;
202 }
203 }
204 }
205 return true;
206 }
207
AddEventType(const std::string & event_name,size_t * group_id)208 bool EventSelectionSet::AddEventType(const std::string& event_name, size_t* group_id) {
209 return AddEventGroup(std::vector<std::string>(1, event_name), group_id);
210 }
211
AddEventGroup(const std::vector<std::string> & event_names,size_t * group_id)212 bool EventSelectionSet::AddEventGroup(
213 const std::vector<std::string>& event_names, size_t* group_id) {
214 EventSelectionGroup group;
215 bool first_event = groups_.empty();
216 for (const auto& event_name : event_names) {
217 EventSelection selection;
218 if (!BuildAndCheckEventSelection(event_name, first_event, &selection)) {
219 return false;
220 }
221 first_event = false;
222 group.push_back(std::move(selection));
223 }
224 groups_.push_back(std::move(group));
225 UnionSampleType();
226 if (group_id != nullptr) {
227 *group_id = groups_.size() - 1;
228 }
229 return true;
230 }
231
GetEvents() const232 std::vector<const EventType*> EventSelectionSet::GetEvents() const {
233 std::vector<const EventType*> result;
234 for (const auto& group : groups_) {
235 for (const auto& selection : group) {
236 result.push_back(&selection.event_type_modifier.event_type);
237 }
238 }
239 return result;
240 }
241
GetTracepointEvents() const242 std::vector<const EventType*> EventSelectionSet::GetTracepointEvents() const {
243 std::vector<const EventType*> result;
244 for (const auto& group : groups_) {
245 for (const auto& selection : group) {
246 if (selection.event_type_modifier.event_type.type ==
247 PERF_TYPE_TRACEPOINT) {
248 result.push_back(&selection.event_type_modifier.event_type);
249 }
250 }
251 }
252 return result;
253 }
254
ExcludeKernel() const255 bool EventSelectionSet::ExcludeKernel() const {
256 for (const auto& group : groups_) {
257 for (const auto& selection : group) {
258 if (!selection.event_type_modifier.exclude_kernel) {
259 return false;
260 }
261 }
262 }
263 return true;
264 }
265
HasInplaceSampler() const266 bool EventSelectionSet::HasInplaceSampler() const {
267 for (const auto& group : groups_) {
268 for (const auto& sel : group) {
269 if (sel.event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS &&
270 sel.event_attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) {
271 return true;
272 }
273 }
274 }
275 return false;
276 }
277
GetEventAttrWithId() const278 std::vector<EventAttrWithId> EventSelectionSet::GetEventAttrWithId() const {
279 std::vector<EventAttrWithId> result;
280 for (const auto& group : groups_) {
281 for (const auto& selection : group) {
282 EventAttrWithId attr_id;
283 attr_id.attr = &selection.event_attr;
284 for (const auto& fd : selection.event_fds) {
285 attr_id.ids.push_back(fd->Id());
286 }
287 if (!selection.inplace_samplers.empty()) {
288 attr_id.ids.push_back(selection.inplace_samplers[0]->Id());
289 }
290 result.push_back(attr_id);
291 }
292 }
293 return result;
294 }
295
296 // Union the sample type of different event attrs can make reading sample
297 // records in perf.data easier.
UnionSampleType()298 void EventSelectionSet::UnionSampleType() {
299 uint64_t sample_type = 0;
300 for (const auto& group : groups_) {
301 for (const auto& selection : group) {
302 sample_type |= selection.event_attr.sample_type;
303 }
304 }
305 for (auto& group : groups_) {
306 for (auto& selection : group) {
307 selection.event_attr.sample_type = sample_type;
308 }
309 }
310 }
311
SetEnableOnExec(bool enable)312 void EventSelectionSet::SetEnableOnExec(bool enable) {
313 for (auto& group : groups_) {
314 for (auto& selection : group) {
315 // If sampling is enabled on exec, then it is disabled at startup,
316 // otherwise it should be enabled at startup. Don't use
317 // ioctl(PERF_EVENT_IOC_ENABLE) to enable it after perf_event_open().
318 // Because some android kernels can't handle ioctl() well when cpu-hotplug
319 // happens. See http://b/25193162.
320 if (enable) {
321 selection.event_attr.enable_on_exec = 1;
322 selection.event_attr.disabled = 1;
323 } else {
324 selection.event_attr.enable_on_exec = 0;
325 selection.event_attr.disabled = 0;
326 }
327 }
328 }
329 }
330
GetEnableOnExec()331 bool EventSelectionSet::GetEnableOnExec() {
332 for (const auto& group : groups_) {
333 for (const auto& selection : group) {
334 if (selection.event_attr.enable_on_exec == 0) {
335 return false;
336 }
337 }
338 }
339 return true;
340 }
341
SampleIdAll()342 void EventSelectionSet::SampleIdAll() {
343 for (auto& group : groups_) {
344 for (auto& selection : group) {
345 selection.event_attr.sample_id_all = 1;
346 }
347 }
348 }
349
SetSampleSpeed(size_t group_id,const SampleSpeed & speed)350 void EventSelectionSet::SetSampleSpeed(size_t group_id, const SampleSpeed& speed) {
351 CHECK_LT(group_id, groups_.size());
352 for (auto& selection : groups_[group_id]) {
353 if (speed.UseFreq()) {
354 selection.event_attr.freq = 1;
355 selection.event_attr.sample_freq = speed.sample_freq;
356 } else {
357 selection.event_attr.freq = 0;
358 selection.event_attr.sample_period = speed.sample_period;
359 }
360 }
361 }
362
SetBranchSampling(uint64_t branch_sample_type)363 bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) {
364 if (branch_sample_type != 0 &&
365 (branch_sample_type &
366 (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
367 PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) {
368 LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex
369 << branch_sample_type;
370 return false;
371 }
372 if (branch_sample_type != 0 && !IsBranchSamplingSupported()) {
373 LOG(ERROR) << "branch stack sampling is not supported on this device.";
374 return false;
375 }
376 for (auto& group : groups_) {
377 for (auto& selection : group) {
378 perf_event_attr& attr = selection.event_attr;
379 if (branch_sample_type != 0) {
380 attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
381 } else {
382 attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK;
383 }
384 attr.branch_sample_type = branch_sample_type;
385 }
386 }
387 return true;
388 }
389
EnableFpCallChainSampling()390 void EventSelectionSet::EnableFpCallChainSampling() {
391 for (auto& group : groups_) {
392 for (auto& selection : group) {
393 selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
394 }
395 }
396 }
397
EnableDwarfCallChainSampling(uint32_t dump_stack_size)398 bool EventSelectionSet::EnableDwarfCallChainSampling(uint32_t dump_stack_size) {
399 if (!IsDwarfCallChainSamplingSupported()) {
400 LOG(ERROR) << "dwarf callchain sampling is not supported on this device.";
401 return false;
402 }
403 for (auto& group : groups_) {
404 for (auto& selection : group) {
405 selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN |
406 PERF_SAMPLE_REGS_USER |
407 PERF_SAMPLE_STACK_USER;
408 selection.event_attr.exclude_callchain_user = 1;
409 selection.event_attr.sample_regs_user =
410 GetSupportedRegMask(GetMachineArch());
411 selection.event_attr.sample_stack_user = dump_stack_size;
412 }
413 }
414 return true;
415 }
416
SetInherit(bool enable)417 void EventSelectionSet::SetInherit(bool enable) {
418 for (auto& group : groups_) {
419 for (auto& selection : group) {
420 selection.event_attr.inherit = (enable ? 1 : 0);
421 }
422 }
423 }
424
SetClockId(int clock_id)425 void EventSelectionSet::SetClockId(int clock_id) {
426 for (auto& group : groups_) {
427 for (auto& selection : group) {
428 selection.event_attr.use_clockid = 1;
429 selection.event_attr.clockid = clock_id;
430 }
431 }
432 }
433
NeedKernelSymbol() const434 bool EventSelectionSet::NeedKernelSymbol() const {
435 for (const auto& group : groups_) {
436 for (const auto& selection : group) {
437 if (!selection.event_type_modifier.exclude_kernel) {
438 return true;
439 }
440 }
441 }
442 return false;
443 }
444
SetRecordNotExecutableMaps(bool record)445 void EventSelectionSet::SetRecordNotExecutableMaps(bool record) {
446 // We only need to dump non-executable mmap records for the first event type.
447 groups_[0][0].event_attr.mmap_data = record ? 1 : 0;
448 }
449
RecordNotExecutableMaps() const450 bool EventSelectionSet::RecordNotExecutableMaps() const {
451 return groups_[0][0].event_attr.mmap_data == 1;
452 }
453
CheckIfCpusOnline(const std::vector<int> & cpus)454 static bool CheckIfCpusOnline(const std::vector<int>& cpus) {
455 std::vector<int> online_cpus = GetOnlineCpus();
456 for (const auto& cpu : cpus) {
457 if (std::find(online_cpus.begin(), online_cpus.end(), cpu) ==
458 online_cpus.end()) {
459 LOG(ERROR) << "cpu " << cpu << " is not online.";
460 return false;
461 }
462 }
463 return true;
464 }
465
OpenEventFilesOnGroup(EventSelectionGroup & group,pid_t tid,int cpu,std::string * failed_event_type)466 bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group,
467 pid_t tid, int cpu,
468 std::string* failed_event_type) {
469 std::vector<std::unique_ptr<EventFd>> event_fds;
470 // Given a tid and cpu, events on the same group should be all opened
471 // successfully or all failed to open.
472 EventFd* group_fd = nullptr;
473 for (auto& selection : group) {
474 std::unique_ptr<EventFd> event_fd =
475 EventFd::OpenEventFile(selection.event_attr, tid, cpu, group_fd, false);
476 if (!event_fd) {
477 *failed_event_type = selection.event_type_modifier.name;
478 return false;
479 }
480 LOG(VERBOSE) << "OpenEventFile for " << event_fd->Name();
481 event_fds.push_back(std::move(event_fd));
482 if (group_fd == nullptr) {
483 group_fd = event_fds.back().get();
484 }
485 }
486 for (size_t i = 0; i < group.size(); ++i) {
487 group[i].event_fds.push_back(std::move(event_fds[i]));
488 }
489 return true;
490 }
491
PrepareThreads(const std::set<pid_t> & processes,const std::set<pid_t> & threads)492 static std::map<pid_t, std::set<pid_t>> PrepareThreads(const std::set<pid_t>& processes,
493 const std::set<pid_t>& threads) {
494 std::map<pid_t, std::set<pid_t>> result;
495 for (auto& pid : processes) {
496 std::vector<pid_t> tids = GetThreadsInProcess(pid);
497 std::set<pid_t>& threads_in_process = result[pid];
498 threads_in_process.insert(tids.begin(), tids.end());
499 }
500 for (auto& tid : threads) {
501 // tid = -1 means monitoring all threads.
502 if (tid == -1) {
503 result[-1].insert(-1);
504 } else {
505 pid_t pid;
506 if (GetProcessForThread(tid, &pid)) {
507 result[pid].insert(tid);
508 }
509 }
510 }
511 return result;
512 }
513
OpenEventFiles(const std::vector<int> & on_cpus)514 bool EventSelectionSet::OpenEventFiles(const std::vector<int>& on_cpus) {
515 std::vector<int> cpus = on_cpus;
516 if (!cpus.empty()) {
517 // cpus = {-1} means open an event file for all cpus.
518 if (!(cpus.size() == 1 && cpus[0] == -1) && !CheckIfCpusOnline(cpus)) {
519 return false;
520 }
521 } else {
522 cpus = GetOnlineCpus();
523 }
524 std::map<pid_t, std::set<pid_t>> process_map = PrepareThreads(processes_, threads_);
525 for (auto& group : groups_) {
526 if (IsUserSpaceSamplerGroup(group)) {
527 if (!OpenUserSpaceSamplersOnGroup(group, process_map)) {
528 return false;
529 }
530 } else {
531 for (const auto& pair : process_map) {
532 size_t success_count = 0;
533 std::string failed_event_type;
534 for (const auto& tid : pair.second) {
535 for (const auto& cpu : cpus) {
536 if (OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
537 success_count++;
538 }
539 }
540 }
541 // We can't guarantee to open perf event file successfully for each thread on each cpu.
542 // Because threads may exit between PrepareThreads() and OpenEventFilesOnGroup(), and
543 // cpus may be offlined between GetOnlineCpus() and OpenEventFilesOnGroup().
544 // So we only check that we can at least monitor one thread for each process.
545 if (success_count == 0) {
546 PLOG(ERROR) << "failed to open perf event file for event_type "
547 << failed_event_type << " for "
548 << (pair.first == -1 ? "all threads"
549 : "threads in process " + std::to_string(pair.first));
550 return false;
551 }
552 }
553 }
554 }
555 return true;
556 }
557
IsUserSpaceSamplerGroup(EventSelectionGroup & group)558 bool EventSelectionSet::IsUserSpaceSamplerGroup(EventSelectionGroup& group) {
559 return group.size() == 1 && group[0].event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS;
560 }
561
OpenUserSpaceSamplersOnGroup(EventSelectionGroup & group,const std::map<pid_t,std::set<pid_t>> & process_map)562 bool EventSelectionSet::OpenUserSpaceSamplersOnGroup(EventSelectionGroup& group,
563 const std::map<pid_t, std::set<pid_t>>& process_map) {
564 CHECK_EQ(group.size(), 1u);
565 for (auto& selection : group) {
566 if (selection.event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS &&
567 selection.event_attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) {
568 for (auto& pair : process_map) {
569 std::unique_ptr<InplaceSamplerClient> sampler = InplaceSamplerClient::Create(
570 selection.event_attr, pair.first, pair.second);
571 if (sampler == nullptr) {
572 return false;
573 }
574 selection.inplace_samplers.push_back(std::move(sampler));
575 }
576 }
577 }
578 return true;
579 }
580
ReadCounter(EventFd * event_fd,CounterInfo * counter)581 static bool ReadCounter(EventFd* event_fd, CounterInfo* counter) {
582 if (!event_fd->ReadCounter(&counter->counter)) {
583 return false;
584 }
585 counter->tid = event_fd->ThreadId();
586 counter->cpu = event_fd->Cpu();
587 return true;
588 }
589
ReadCounters(std::vector<CountersInfo> * counters)590 bool EventSelectionSet::ReadCounters(std::vector<CountersInfo>* counters) {
591 counters->clear();
592 for (size_t i = 0; i < groups_.size(); ++i) {
593 for (auto& selection : groups_[i]) {
594 CountersInfo counters_info;
595 counters_info.group_id = i;
596 counters_info.event_name = selection.event_type_modifier.event_type.name;
597 counters_info.event_modifier = selection.event_type_modifier.modifier;
598 counters_info.counters = selection.hotplugged_counters;
599 for (auto& event_fd : selection.event_fds) {
600 CounterInfo counter;
601 if (!ReadCounter(event_fd.get(), &counter)) {
602 return false;
603 }
604 counters_info.counters.push_back(counter);
605 }
606 counters->push_back(counters_info);
607 }
608 }
609 return true;
610 }
611
MmapEventFiles(size_t min_mmap_pages,size_t max_mmap_pages,size_t record_buffer_size)612 bool EventSelectionSet::MmapEventFiles(size_t min_mmap_pages, size_t max_mmap_pages,
613 size_t record_buffer_size) {
614 record_read_thread_.reset(new simpleperf::RecordReadThread(
615 record_buffer_size, groups_[0][0].event_attr, min_mmap_pages, max_mmap_pages));
616 return true;
617 }
618
PrepareToReadMmapEventData(const std::function<bool (Record *)> & callback)619 bool EventSelectionSet::PrepareToReadMmapEventData(const std::function<bool(Record*)>& callback) {
620 // Prepare record callback function.
621 record_callback_ = callback;
622 if (!record_read_thread_->RegisterDataCallback(*loop_,
623 [this]() { return ReadMmapEventData(true); })) {
624 return false;
625 }
626 std::vector<EventFd*> event_fds;
627 for (auto& group : groups_) {
628 for (auto& selection : group) {
629 for (auto& event_fd : selection.event_fds) {
630 event_fds.push_back(event_fd.get());
631 }
632 }
633 }
634 return record_read_thread_->AddEventFds(event_fds);
635 }
636
SyncKernelBuffer()637 bool EventSelectionSet::SyncKernelBuffer() {
638 return record_read_thread_->SyncKernelBuffer();
639 }
640
641 // Read records from the RecordBuffer. If with_time_limit is false, read until the RecordBuffer is
642 // empty, otherwise stop after 100 ms or when the record buffer is empty.
ReadMmapEventData(bool with_time_limit)643 bool EventSelectionSet::ReadMmapEventData(bool with_time_limit) {
644 uint64_t start_time_in_ns;
645 if (with_time_limit) {
646 start_time_in_ns = GetSystemClock();
647 }
648 std::unique_ptr<Record> r;
649 while ((r = record_read_thread_->GetRecord()) != nullptr) {
650 if (!record_callback_(r.get())) {
651 return false;
652 }
653 if (with_time_limit && (GetSystemClock() - start_time_in_ns) >= 1e8) {
654 break;
655 }
656 }
657 return true;
658 }
659
FinishReadMmapEventData()660 bool EventSelectionSet::FinishReadMmapEventData() {
661 // Stop the read thread, so we don't get more records beyond current time.
662 if (!SyncKernelBuffer() || !record_read_thread_->StopReadThread()) {
663 return false;
664 }
665 if (!ReadMmapEventData(false)) {
666 return false;
667 }
668 if (!HasInplaceSampler()) {
669 return true;
670 }
671 // Inplace sampler server uses a buffer to cache samples before sending them, so we need to
672 // explicitly ask it to send the cached samples.
673 loop_.reset(new IOEventLoop);
674 size_t inplace_sampler_count = 0;
675 auto close_callback = [&]() {
676 if (--inplace_sampler_count == 0) {
677 return loop_->ExitLoop();
678 }
679 return true;
680 };
681 for (auto& group : groups_) {
682 for (auto& sel : group) {
683 for (auto& sampler : sel.inplace_samplers) {
684 if (!sampler->IsClosed()) {
685 if (!sampler->StopProfiling(*loop_, close_callback)) {
686 return false;
687 }
688 inplace_sampler_count++;
689 }
690 }
691 }
692 }
693 if (inplace_sampler_count == 0) {
694 return true;
695 }
696
697 // Set a timeout to exit the loop.
698 timeval tv;
699 tv.tv_sec = 1;
700 tv.tv_usec = 0;
701 if (!loop_->AddPeriodicEvent(tv, [&]() { return loop_->ExitLoop(); })) {
702 return false;
703 }
704 return loop_->RunLoop();
705 }
706
GetLostRecords(size_t * lost_samples,size_t * lost_non_samples,size_t * cut_stack_samples)707 void EventSelectionSet::GetLostRecords(size_t* lost_samples, size_t* lost_non_samples,
708 size_t* cut_stack_samples) {
709 record_read_thread_->GetLostRecords(lost_samples, lost_non_samples, cut_stack_samples);
710 }
711
HandleCpuHotplugEvents(const std::vector<int> & monitored_cpus,double check_interval_in_sec)712 bool EventSelectionSet::HandleCpuHotplugEvents(const std::vector<int>& monitored_cpus,
713 double check_interval_in_sec) {
714 monitored_cpus_.insert(monitored_cpus.begin(), monitored_cpus.end());
715 online_cpus_ = GetOnlineCpus();
716 if (!loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec),
717 [&]() { return DetectCpuHotplugEvents(); })) {
718 return false;
719 }
720 return true;
721 }
722
DetectCpuHotplugEvents()723 bool EventSelectionSet::DetectCpuHotplugEvents() {
724 std::vector<int> new_cpus = GetOnlineCpus();
725 for (const auto& cpu : online_cpus_) {
726 if (std::find(new_cpus.begin(), new_cpus.end(), cpu) == new_cpus.end()) {
727 if (monitored_cpus_.empty() ||
728 monitored_cpus_.find(cpu) != monitored_cpus_.end()) {
729 LOG(INFO) << "Cpu " << cpu << " is offlined";
730 if (!HandleCpuOfflineEvent(cpu)) {
731 return false;
732 }
733 }
734 }
735 }
736 for (const auto& cpu : new_cpus) {
737 if (std::find(online_cpus_.begin(), online_cpus_.end(), cpu) ==
738 online_cpus_.end()) {
739 if (monitored_cpus_.empty() ||
740 monitored_cpus_.find(cpu) != monitored_cpus_.end()) {
741 LOG(INFO) << "Cpu " << cpu << " is onlined";
742 if (!HandleCpuOnlineEvent(cpu)) {
743 return false;
744 }
745 }
746 }
747 }
748 online_cpus_ = new_cpus;
749 return true;
750 }
751
HandleCpuOfflineEvent(int cpu)752 bool EventSelectionSet::HandleCpuOfflineEvent(int cpu) {
753 if (!for_stat_cmd_) {
754 // Read mmap data here, so we won't lose the existing records of the
755 // offlined cpu.
756 if (!ReadMmapEventData(true)) {
757 return false;
758 }
759 }
760 if (record_read_thread_) {
761 std::vector<EventFd*> remove_fds;
762 for (auto& group : groups_) {
763 for (auto& selection : group) {
764 for (auto& fd : selection.event_fds) {
765 if (fd->Cpu() == cpu) {
766 remove_fds.push_back(fd.get());
767 }
768 }
769 }
770 }
771 if (!record_read_thread_->RemoveEventFds(remove_fds)) {
772 return false;
773 }
774 }
775 for (auto& group : groups_) {
776 for (auto& selection : group) {
777 for (auto it = selection.event_fds.begin(); it != selection.event_fds.end();) {
778 if ((*it)->Cpu() == cpu) {
779 if (for_stat_cmd_) {
780 CounterInfo counter;
781 if (!ReadCounter(it->get(), &counter)) {
782 return false;
783 }
784 selection.hotplugged_counters.push_back(counter);
785 }
786 it = selection.event_fds.erase(it);
787 } else {
788 ++it;
789 }
790 }
791 }
792 }
793 return true;
794 }
795
HandleCpuOnlineEvent(int cpu)796 bool EventSelectionSet::HandleCpuOnlineEvent(int cpu) {
797 // We need to start profiling when opening new event files.
798 SetEnableOnExec(false);
799 std::map<pid_t, std::set<pid_t>> process_map = PrepareThreads(processes_, threads_);
800 for (auto& group : groups_) {
801 if (IsUserSpaceSamplerGroup(group)) {
802 continue;
803 }
804 for (const auto& pair : process_map) {
805 for (const auto& tid : pair.second) {
806 std::string failed_event_type;
807 if (!OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
808 // If failed to open event files, maybe the cpu has been offlined.
809 PLOG(WARNING) << "failed to open perf event file for event_type "
810 << failed_event_type << " for "
811 << (tid == -1 ? "all threads" : "thread " + std::to_string(tid))
812 << " on cpu " << cpu;
813 }
814 }
815 }
816 }
817 if (record_read_thread_) {
818 // Prepare mapped buffer.
819 if (!CreateMappedBufferForCpu(cpu)) {
820 return false;
821 }
822 // Send a EventIdRecord.
823 std::vector<uint64_t> event_id_data;
824 uint64_t attr_id = 0;
825 for (const auto& group : groups_) {
826 for (const auto& selection : group) {
827 for (const auto& event_fd : selection.event_fds) {
828 if (event_fd->Cpu() == cpu) {
829 event_id_data.push_back(attr_id);
830 event_id_data.push_back(event_fd->Id());
831 }
832 }
833 ++attr_id;
834 }
835 }
836 EventIdRecord r(event_id_data);
837 if (!record_callback_(&r)) {
838 return false;
839 }
840 }
841 return true;
842 }
843
CreateMappedBufferForCpu(int cpu)844 bool EventSelectionSet::CreateMappedBufferForCpu(int cpu) {
845 std::vector<EventFd*> event_fds;
846 for (auto& group : groups_) {
847 for (auto& selection : group) {
848 for (auto& fd : selection.event_fds) {
849 if (fd->Cpu() == cpu) {
850 event_fds.push_back(fd.get());
851 }
852 }
853 }
854 }
855 return record_read_thread_->AddEventFds(event_fds);
856 }
857
StopWhenNoMoreTargets(double check_interval_in_sec)858 bool EventSelectionSet::StopWhenNoMoreTargets(double check_interval_in_sec) {
859 return loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec),
860 [&]() { return CheckMonitoredTargets(); });
861 }
862
CheckMonitoredTargets()863 bool EventSelectionSet::CheckMonitoredTargets() {
864 if (!HasSampler()) {
865 return loop_->ExitLoop();
866 }
867 for (const auto& tid : threads_) {
868 if (IsThreadAlive(tid)) {
869 return true;
870 }
871 }
872 for (const auto& pid : processes_) {
873 if (IsThreadAlive(pid)) {
874 return true;
875 }
876 }
877 return loop_->ExitLoop();
878 }
879
HasSampler()880 bool EventSelectionSet::HasSampler() {
881 for (auto& group : groups_) {
882 for (auto& sel : group) {
883 if (!sel.event_fds.empty()) {
884 return true;
885 }
886 for (auto& sampler : sel.inplace_samplers) {
887 if (!sampler->IsClosed()) {
888 return true;
889 }
890 }
891 }
892 }
893 return false;
894 }
895
SetEnableEvents(bool enable)896 bool EventSelectionSet::SetEnableEvents(bool enable) {
897 for (auto& group : groups_) {
898 for (auto& sel : group) {
899 for (auto& fd : sel.event_fds) {
900 if (!fd->SetEnableEvent(enable)) {
901 return false;
902 }
903 }
904 }
905 }
906 return true;
907 }
908