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 <android-base/logging.h>
20
21 #include "environment.h"
22 #include "event_attr.h"
23 #include "event_type.h"
24 #include "IOEventLoop.h"
25 #include "perf_regs.h"
26 #include "utils.h"
27
28 constexpr uint64_t DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT = 4000;
29 constexpr uint64_t DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT = 1;
30
IsBranchSamplingSupported()31 bool IsBranchSamplingSupported() {
32 const EventType* type = FindEventTypeByName("cpu-cycles");
33 if (type == nullptr) {
34 return false;
35 }
36 perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
37 attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
38 attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
39 return IsEventAttrSupported(attr);
40 }
41
IsDwarfCallChainSamplingSupported()42 bool IsDwarfCallChainSamplingSupported() {
43 const EventType* type = FindEventTypeByName("cpu-cycles");
44 if (type == nullptr) {
45 return false;
46 }
47 perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
48 attr.sample_type |=
49 PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER;
50 attr.exclude_callchain_user = 1;
51 attr.sample_regs_user = GetSupportedRegMask(GetBuildArch());
52 attr.sample_stack_user = 8192;
53 return IsEventAttrSupported(attr);
54 }
55
BuildAndCheckEventSelection(const std::string & event_name,EventSelection * selection)56 bool EventSelectionSet::BuildAndCheckEventSelection(
57 const std::string& event_name, EventSelection* selection) {
58 std::unique_ptr<EventTypeAndModifier> event_type = ParseEventType(event_name);
59 if (event_type == nullptr) {
60 return false;
61 }
62 if (for_stat_cmd_) {
63 if (event_type->event_type.name == "cpu-clock" ||
64 event_type->event_type.name == "task-clock") {
65 if (event_type->exclude_user || event_type->exclude_kernel) {
66 LOG(ERROR) << "Modifier u and modifier k used in event type "
67 << event_type->event_type.name
68 << " are not supported by the kernel.";
69 return false;
70 }
71 }
72 }
73 selection->event_type_modifier = *event_type;
74 selection->event_attr = CreateDefaultPerfEventAttr(event_type->event_type);
75 selection->event_attr.exclude_user = event_type->exclude_user;
76 selection->event_attr.exclude_kernel = event_type->exclude_kernel;
77 selection->event_attr.exclude_hv = event_type->exclude_hv;
78 selection->event_attr.exclude_host = event_type->exclude_host;
79 selection->event_attr.exclude_guest = event_type->exclude_guest;
80 selection->event_attr.precise_ip = event_type->precise_ip;
81 if (!IsEventAttrSupported(selection->event_attr)) {
82 LOG(ERROR) << "Event type '" << event_type->name
83 << "' is not supported on the device";
84 return false;
85 }
86 selection->event_fds.clear();
87
88 for (const auto& group : groups_) {
89 for (const auto& sel : group) {
90 if (sel.event_type_modifier.name == selection->event_type_modifier.name) {
91 LOG(ERROR) << "Event type '" << sel.event_type_modifier.name
92 << "' appears more than once";
93 return false;
94 }
95 }
96 }
97 return true;
98 }
99
AddEventType(const std::string & event_name)100 bool EventSelectionSet::AddEventType(const std::string& event_name) {
101 return AddEventGroup(std::vector<std::string>(1, event_name));
102 }
103
AddEventGroup(const std::vector<std::string> & event_names)104 bool EventSelectionSet::AddEventGroup(
105 const std::vector<std::string>& event_names) {
106 EventSelectionGroup group;
107 for (const auto& event_name : event_names) {
108 EventSelection selection;
109 if (!BuildAndCheckEventSelection(event_name, &selection)) {
110 return false;
111 }
112 group.push_back(std::move(selection));
113 }
114 groups_.push_back(std::move(group));
115 UnionSampleType();
116 return true;
117 }
118
GetTracepointEvents() const119 std::vector<const EventType*> EventSelectionSet::GetTracepointEvents() const {
120 std::vector<const EventType*> result;
121 for (const auto& group : groups_) {
122 for (const auto& selection : group) {
123 if (selection.event_type_modifier.event_type.type ==
124 PERF_TYPE_TRACEPOINT) {
125 result.push_back(&selection.event_type_modifier.event_type);
126 }
127 }
128 }
129 return result;
130 }
131
HasInplaceSampler() const132 bool EventSelectionSet::HasInplaceSampler() const {
133 for (const auto& group : groups_) {
134 for (const auto& sel : group) {
135 if (sel.event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS &&
136 sel.event_attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) {
137 return true;
138 }
139 }
140 }
141 return false;
142 }
143
GetEventAttrWithId() const144 std::vector<EventAttrWithId> EventSelectionSet::GetEventAttrWithId() const {
145 std::vector<EventAttrWithId> result;
146 for (const auto& group : groups_) {
147 for (const auto& selection : group) {
148 EventAttrWithId attr_id;
149 attr_id.attr = &selection.event_attr;
150 for (const auto& fd : selection.event_fds) {
151 attr_id.ids.push_back(fd->Id());
152 }
153 if (!selection.inplace_samplers.empty()) {
154 attr_id.ids.push_back(selection.inplace_samplers[0]->Id());
155 }
156 result.push_back(attr_id);
157 }
158 }
159 return result;
160 }
161
162 // Union the sample type of different event attrs can make reading sample
163 // records in perf.data easier.
UnionSampleType()164 void EventSelectionSet::UnionSampleType() {
165 uint64_t sample_type = 0;
166 for (const auto& group : groups_) {
167 for (const auto& selection : group) {
168 sample_type |= selection.event_attr.sample_type;
169 }
170 }
171 for (auto& group : groups_) {
172 for (auto& selection : group) {
173 selection.event_attr.sample_type = sample_type;
174 }
175 }
176 }
177
SetEnableOnExec(bool enable)178 void EventSelectionSet::SetEnableOnExec(bool enable) {
179 for (auto& group : groups_) {
180 for (auto& selection : group) {
181 // If sampling is enabled on exec, then it is disabled at startup,
182 // otherwise it should be enabled at startup. Don't use
183 // ioctl(PERF_EVENT_IOC_ENABLE) to enable it after perf_event_open().
184 // Because some android kernels can't handle ioctl() well when cpu-hotplug
185 // happens. See http://b/25193162.
186 if (enable) {
187 selection.event_attr.enable_on_exec = 1;
188 selection.event_attr.disabled = 1;
189 } else {
190 selection.event_attr.enable_on_exec = 0;
191 selection.event_attr.disabled = 0;
192 }
193 }
194 }
195 }
196
GetEnableOnExec()197 bool EventSelectionSet::GetEnableOnExec() {
198 for (const auto& group : groups_) {
199 for (const auto& selection : group) {
200 if (selection.event_attr.enable_on_exec == 0) {
201 return false;
202 }
203 }
204 }
205 return true;
206 }
207
SampleIdAll()208 void EventSelectionSet::SampleIdAll() {
209 for (auto& group : groups_) {
210 for (auto& selection : group) {
211 selection.event_attr.sample_id_all = 1;
212 }
213 }
214 }
215
SetSampleFreq(uint64_t sample_freq)216 void EventSelectionSet::SetSampleFreq(uint64_t sample_freq) {
217 for (auto& group : groups_) {
218 for (auto& selection : group) {
219 selection.event_attr.freq = 1;
220 selection.event_attr.sample_freq = sample_freq;
221 }
222 }
223 }
224
SetSamplePeriod(uint64_t sample_period)225 void EventSelectionSet::SetSamplePeriod(uint64_t sample_period) {
226 for (auto& group : groups_) {
227 for (auto& selection : group) {
228 selection.event_attr.freq = 0;
229 selection.event_attr.sample_period = sample_period;
230 }
231 }
232 }
233
UseDefaultSampleFreq()234 void EventSelectionSet::UseDefaultSampleFreq() {
235 for (auto& group : groups_) {
236 for (auto& selection : group) {
237 if (selection.event_type_modifier.event_type.type ==
238 PERF_TYPE_TRACEPOINT) {
239 selection.event_attr.freq = 0;
240 selection.event_attr.sample_period =
241 DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT;
242 } else {
243 selection.event_attr.freq = 1;
244 selection.event_attr.sample_freq =
245 DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT;
246 }
247 }
248 }
249 }
250
SetBranchSampling(uint64_t branch_sample_type)251 bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) {
252 if (branch_sample_type != 0 &&
253 (branch_sample_type &
254 (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
255 PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) {
256 LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex
257 << branch_sample_type;
258 return false;
259 }
260 if (branch_sample_type != 0 && !IsBranchSamplingSupported()) {
261 LOG(ERROR) << "branch stack sampling is not supported on this device.";
262 return false;
263 }
264 for (auto& group : groups_) {
265 for (auto& selection : group) {
266 perf_event_attr& attr = selection.event_attr;
267 if (branch_sample_type != 0) {
268 attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
269 } else {
270 attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK;
271 }
272 attr.branch_sample_type = branch_sample_type;
273 }
274 }
275 return true;
276 }
277
EnableFpCallChainSampling()278 void EventSelectionSet::EnableFpCallChainSampling() {
279 for (auto& group : groups_) {
280 for (auto& selection : group) {
281 selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
282 }
283 }
284 }
285
EnableDwarfCallChainSampling(uint32_t dump_stack_size)286 bool EventSelectionSet::EnableDwarfCallChainSampling(uint32_t dump_stack_size) {
287 if (!IsDwarfCallChainSamplingSupported()) {
288 LOG(ERROR) << "dwarf callchain sampling is not supported on this device.";
289 return false;
290 }
291 for (auto& group : groups_) {
292 for (auto& selection : group) {
293 selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN |
294 PERF_SAMPLE_REGS_USER |
295 PERF_SAMPLE_STACK_USER;
296 selection.event_attr.exclude_callchain_user = 1;
297 selection.event_attr.sample_regs_user =
298 GetSupportedRegMask(GetMachineArch());
299 selection.event_attr.sample_stack_user = dump_stack_size;
300 }
301 }
302 return true;
303 }
304
SetInherit(bool enable)305 void EventSelectionSet::SetInherit(bool enable) {
306 for (auto& group : groups_) {
307 for (auto& selection : group) {
308 selection.event_attr.inherit = (enable ? 1 : 0);
309 }
310 }
311 }
312
NeedKernelSymbol() const313 bool EventSelectionSet::NeedKernelSymbol() const {
314 for (const auto& group : groups_) {
315 for (const auto& selection : group) {
316 if (!selection.event_type_modifier.exclude_kernel) {
317 return true;
318 }
319 }
320 }
321 return false;
322 }
323
CheckIfCpusOnline(const std::vector<int> & cpus)324 static bool CheckIfCpusOnline(const std::vector<int>& cpus) {
325 std::vector<int> online_cpus = GetOnlineCpus();
326 for (const auto& cpu : cpus) {
327 if (std::find(online_cpus.begin(), online_cpus.end(), cpu) ==
328 online_cpus.end()) {
329 LOG(ERROR) << "cpu " << cpu << " is not online.";
330 return false;
331 }
332 }
333 return true;
334 }
335
OpenEventFilesOnGroup(EventSelectionGroup & group,pid_t tid,int cpu,std::string * failed_event_type)336 bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group,
337 pid_t tid, int cpu,
338 std::string* failed_event_type) {
339 std::vector<std::unique_ptr<EventFd>> event_fds;
340 // Given a tid and cpu, events on the same group should be all opened
341 // successfully or all failed to open.
342 EventFd* group_fd = nullptr;
343 for (auto& selection : group) {
344 std::unique_ptr<EventFd> event_fd =
345 EventFd::OpenEventFile(selection.event_attr, tid, cpu, group_fd);
346 if (event_fd != nullptr) {
347 LOG(VERBOSE) << "OpenEventFile for " << event_fd->Name();
348 event_fds.push_back(std::move(event_fd));
349 } else {
350 if (failed_event_type != nullptr) {
351 *failed_event_type = selection.event_type_modifier.name;
352 return false;
353 }
354 }
355 if (group_fd == nullptr) {
356 group_fd = event_fd.get();
357 }
358 }
359 for (size_t i = 0; i < group.size(); ++i) {
360 group[i].event_fds.push_back(std::move(event_fds[i]));
361 }
362 return true;
363 }
364
PrepareThreads(const std::set<pid_t> & processes,const std::set<pid_t> & threads)365 static std::map<pid_t, std::set<pid_t>> PrepareThreads(const std::set<pid_t>& processes,
366 const std::set<pid_t>& threads) {
367 std::map<pid_t, std::set<pid_t>> result;
368 for (auto& pid : processes) {
369 std::vector<pid_t> tids = GetThreadsInProcess(pid);
370 std::set<pid_t>& threads_in_process = result[pid];
371 threads_in_process.insert(tids.begin(), tids.end());
372 }
373 for (auto& tid : threads) {
374 // tid = -1 means monitoring all threads.
375 if (tid == -1) {
376 result[-1].insert(-1);
377 } else {
378 pid_t pid;
379 if (GetProcessForThread(tid, &pid)) {
380 result[pid].insert(tid);
381 }
382 }
383 }
384 return result;
385 }
386
OpenEventFiles(const std::vector<int> & on_cpus)387 bool EventSelectionSet::OpenEventFiles(const std::vector<int>& on_cpus) {
388 std::vector<int> cpus = on_cpus;
389 if (!cpus.empty()) {
390 // cpus = {-1} means open an event file for all cpus.
391 if (!(cpus.size() == 1 && cpus[0] == -1) && !CheckIfCpusOnline(cpus)) {
392 return false;
393 }
394 } else {
395 cpus = GetOnlineCpus();
396 }
397 std::map<pid_t, std::set<pid_t>> process_map = PrepareThreads(processes_, threads_);
398 for (auto& group : groups_) {
399 if (IsUserSpaceSamplerGroup(group)) {
400 if (!OpenUserSpaceSamplersOnGroup(group, process_map)) {
401 return false;
402 }
403 } else {
404 for (const auto& pair : process_map) {
405 for (const auto& tid : pair.second) {
406 size_t success_cpu_count = 0;
407 std::string failed_event_type;
408 for (const auto& cpu : cpus) {
409 if (OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
410 success_cpu_count++;
411 }
412 }
413 // As the online cpus can be enabled or disabled at runtime, we may not
414 // open event file for all cpus successfully. But we should open at
415 // least one cpu successfully.
416 if (success_cpu_count == 0) {
417 PLOG(ERROR) << "failed to open perf event file for event_type "
418 << failed_event_type << " for "
419 << (tid == -1 ? "all threads" : "thread " + std::to_string(tid))
420 << " on all cpus";
421 return false;
422 }
423 }
424 }
425 }
426 }
427 return true;
428 }
429
IsUserSpaceSamplerGroup(EventSelectionGroup & group)430 bool EventSelectionSet::IsUserSpaceSamplerGroup(EventSelectionGroup& group) {
431 return group.size() == 1 && group[0].event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS;
432 }
433
OpenUserSpaceSamplersOnGroup(EventSelectionGroup & group,const std::map<pid_t,std::set<pid_t>> & process_map)434 bool EventSelectionSet::OpenUserSpaceSamplersOnGroup(EventSelectionGroup& group,
435 const std::map<pid_t, std::set<pid_t>>& process_map) {
436 CHECK_EQ(group.size(), 1u);
437 for (auto& selection : group) {
438 if (selection.event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS &&
439 selection.event_attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) {
440 for (auto& pair : process_map) {
441 std::unique_ptr<InplaceSamplerClient> sampler = InplaceSamplerClient::Create(
442 selection.event_attr, pair.first, pair.second);
443 if (sampler == nullptr) {
444 return false;
445 }
446 selection.inplace_samplers.push_back(std::move(sampler));
447 }
448 }
449 }
450 return true;
451 }
452
ReadCounter(const EventFd * event_fd,CounterInfo * counter)453 static bool ReadCounter(const EventFd* event_fd, CounterInfo* counter) {
454 if (!event_fd->ReadCounter(&counter->counter)) {
455 return false;
456 }
457 counter->tid = event_fd->ThreadId();
458 counter->cpu = event_fd->Cpu();
459 return true;
460 }
461
ReadCounters(std::vector<CountersInfo> * counters)462 bool EventSelectionSet::ReadCounters(std::vector<CountersInfo>* counters) {
463 counters->clear();
464 for (size_t i = 0; i < groups_.size(); ++i) {
465 for (auto& selection : groups_[i]) {
466 CountersInfo counters_info;
467 counters_info.group_id = i;
468 counters_info.event_name = selection.event_type_modifier.event_type.name;
469 counters_info.event_modifier = selection.event_type_modifier.modifier;
470 counters_info.counters = selection.hotplugged_counters;
471 for (auto& event_fd : selection.event_fds) {
472 CounterInfo counter;
473 if (!ReadCounter(event_fd.get(), &counter)) {
474 return false;
475 }
476 counters_info.counters.push_back(counter);
477 }
478 counters->push_back(counters_info);
479 }
480 }
481 return true;
482 }
483
MmapEventFiles(size_t min_mmap_pages,size_t max_mmap_pages)484 bool EventSelectionSet::MmapEventFiles(size_t min_mmap_pages,
485 size_t max_mmap_pages) {
486 for (size_t i = max_mmap_pages; i >= min_mmap_pages; i >>= 1) {
487 if (MmapEventFiles(i, i == min_mmap_pages)) {
488 LOG(VERBOSE) << "Mapped buffer size is " << i << " pages.";
489 mmap_pages_ = i;
490 return true;
491 }
492 for (auto& group : groups_) {
493 for (auto& selection : group) {
494 for (auto& event_fd : selection.event_fds) {
495 event_fd->DestroyMappedBuffer();
496 }
497 }
498 }
499 }
500 return false;
501 }
502
MmapEventFiles(size_t mmap_pages,bool report_error)503 bool EventSelectionSet::MmapEventFiles(size_t mmap_pages, bool report_error) {
504 // Allocate a mapped buffer for each cpu.
505 std::map<int, EventFd*> cpu_map;
506 for (auto& group : groups_) {
507 for (auto& selection : group) {
508 for (auto& event_fd : selection.event_fds) {
509 auto it = cpu_map.find(event_fd->Cpu());
510 if (it != cpu_map.end()) {
511 if (!event_fd->ShareMappedBuffer(*(it->second), report_error)) {
512 return false;
513 }
514 } else {
515 if (!event_fd->CreateMappedBuffer(mmap_pages, report_error)) {
516 return false;
517 }
518 cpu_map[event_fd->Cpu()] = event_fd.get();
519 }
520 }
521 }
522 }
523 return true;
524 }
525
PrepareToReadMmapEventData(const std::function<bool (Record *)> & callback)526 bool EventSelectionSet::PrepareToReadMmapEventData(const std::function<bool(Record*)>& callback) {
527 // Add read Events for perf event files having mapped buffer.
528 for (auto& group : groups_) {
529 for (auto& selection : group) {
530 for (auto& event_fd : selection.event_fds) {
531 if (event_fd->HasMappedBuffer()) {
532 if (!event_fd->StartPolling(*loop_, [this]() {
533 return ReadMmapEventData();
534 })) {
535 return false;
536 }
537 }
538 }
539 for (auto& sampler : selection.inplace_samplers) {
540 if (!sampler->StartPolling(*loop_, callback,
541 [&] { return CheckMonitoredTargets(); })) {
542 return false;
543 }
544 }
545 }
546 }
547
548 // Prepare record callback function.
549 record_callback_ = callback;
550 return true;
551 }
552
553 // When reading from mmap buffers, we prefer reading from all buffers at once
554 // rather than reading one buffer at a time. Because by reading all buffers
555 // at once, we can merge records from different buffers easily in memory.
556 // Otherwise, we have to sort records with greater effort.
ReadMmapEventData()557 bool EventSelectionSet::ReadMmapEventData() {
558 size_t head_size = 0;
559 std::vector<RecordBufferHead>& heads = record_buffer_heads_;
560 if (heads.empty()) {
561 heads.resize(1);
562 }
563 heads[0].current_pos = 0;
564 size_t buffer_pos = 0;
565
566 for (auto& group : groups_) {
567 for (auto& selection : group) {
568 for (auto& event_fd : selection.event_fds) {
569 if (event_fd->HasMappedBuffer()) {
570 if (event_fd->GetAvailableMmapData(record_buffer_, buffer_pos) != 0) {
571 heads[head_size].end_pos = buffer_pos;
572 heads[head_size].attr = &selection.event_attr;
573 head_size++;
574 if (heads.size() == head_size) {
575 heads.resize(head_size + 1);
576 }
577 heads[head_size].current_pos = buffer_pos;
578 }
579 }
580 }
581 }
582 }
583
584 if (head_size == 0) {
585 return true;
586 }
587 if (head_size == 1) {
588 // Only one buffer has data, process it directly.
589 std::vector<std::unique_ptr<Record>> records =
590 ReadRecordsFromBuffer(*heads[0].attr,
591 record_buffer_.data(), buffer_pos);
592 for (auto& r : records) {
593 if (!record_callback_(r.get())) {
594 return false;
595 }
596 }
597 } else {
598 // Use a priority queue to merge records from different buffers. As
599 // records from the same buffer are already ordered by time, we only
600 // need to merge the first record from all buffers. And each time a
601 // record is popped from the queue, we put the next record from its
602 // buffer into the queue.
603 auto comparator = [&](RecordBufferHead* h1, RecordBufferHead* h2) {
604 return h1->timestamp > h2->timestamp;
605 };
606 std::priority_queue<RecordBufferHead*, std::vector<RecordBufferHead*>, decltype(comparator)> q(comparator);
607 for (size_t i = 0; i < head_size; ++i) {
608 RecordBufferHead& h = heads[i];
609 h.r = ReadRecordFromBuffer(*h.attr, &record_buffer_[h.current_pos]);
610 h.timestamp = h.r->Timestamp();
611 h.current_pos += h.r->size();
612 q.push(&h);
613 }
614 while (!q.empty()) {
615 RecordBufferHead* h = q.top();
616 q.pop();
617 if (!record_callback_(h->r.get())) {
618 return false;
619 }
620 if (h->current_pos < h->end_pos) {
621 h->r = ReadRecordFromBuffer(*h->attr, &record_buffer_[h->current_pos]);
622 h->timestamp = h->r->Timestamp();
623 h->current_pos += h->r->size();
624 q.push(h);
625 }
626 }
627 }
628 return true;
629 }
630
FinishReadMmapEventData()631 bool EventSelectionSet::FinishReadMmapEventData() {
632 if (!ReadMmapEventData()) {
633 return false;
634 }
635 if (!HasInplaceSampler()) {
636 return true;
637 }
638 // Inplace sampler server uses a buffer to cache samples before sending them, so we need to
639 // explicitly ask it to send the cached samples.
640 loop_.reset(new IOEventLoop);
641 size_t inplace_sampler_count = 0;
642 auto close_callback = [&]() {
643 if (--inplace_sampler_count == 0) {
644 return loop_->ExitLoop();
645 }
646 return true;
647 };
648 for (auto& group : groups_) {
649 for (auto& sel : group) {
650 for (auto& sampler : sel.inplace_samplers) {
651 if (!sampler->IsClosed()) {
652 if (!sampler->StopProfiling(*loop_, close_callback)) {
653 return false;
654 }
655 inplace_sampler_count++;
656 }
657 }
658 }
659 }
660 if (inplace_sampler_count == 0) {
661 return true;
662 }
663
664 // Set a timeout to exit the loop.
665 timeval tv;
666 tv.tv_sec = 1;
667 tv.tv_usec = 0;
668 if (!loop_->AddPeriodicEvent(tv, [&]() { return loop_->ExitLoop(); })) {
669 return false;
670 }
671 return loop_->RunLoop();
672 }
673
HandleCpuHotplugEvents(const std::vector<int> & monitored_cpus,double check_interval_in_sec)674 bool EventSelectionSet::HandleCpuHotplugEvents(const std::vector<int>& monitored_cpus,
675 double check_interval_in_sec) {
676 monitored_cpus_.insert(monitored_cpus.begin(), monitored_cpus.end());
677 online_cpus_ = GetOnlineCpus();
678 if (!loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec),
679 [&]() { return DetectCpuHotplugEvents(); })) {
680 return false;
681 }
682 return true;
683 }
684
DetectCpuHotplugEvents()685 bool EventSelectionSet::DetectCpuHotplugEvents() {
686 std::vector<int> new_cpus = GetOnlineCpus();
687 for (const auto& cpu : online_cpus_) {
688 if (std::find(new_cpus.begin(), new_cpus.end(), cpu) == new_cpus.end()) {
689 if (monitored_cpus_.empty() ||
690 monitored_cpus_.find(cpu) != monitored_cpus_.end()) {
691 LOG(INFO) << "Cpu " << cpu << " is offlined";
692 if (!HandleCpuOfflineEvent(cpu)) {
693 return false;
694 }
695 }
696 }
697 }
698 for (const auto& cpu : new_cpus) {
699 if (std::find(online_cpus_.begin(), online_cpus_.end(), cpu) ==
700 online_cpus_.end()) {
701 if (monitored_cpus_.empty() ||
702 monitored_cpus_.find(cpu) != monitored_cpus_.end()) {
703 LOG(INFO) << "Cpu " << cpu << " is onlined";
704 if (!HandleCpuOnlineEvent(cpu)) {
705 return false;
706 }
707 }
708 }
709 }
710 online_cpus_ = new_cpus;
711 return true;
712 }
713
HandleCpuOfflineEvent(int cpu)714 bool EventSelectionSet::HandleCpuOfflineEvent(int cpu) {
715 if (!for_stat_cmd_) {
716 // Read mmap data here, so we won't lose the existing records of the
717 // offlined cpu.
718 if (!ReadMmapEventData()) {
719 return false;
720 }
721 }
722 for (auto& group : groups_) {
723 for (auto& selection : group) {
724 for (auto it = selection.event_fds.begin();
725 it != selection.event_fds.end();) {
726 if ((*it)->Cpu() == cpu) {
727 if (for_stat_cmd_) {
728 CounterInfo counter;
729 if (!ReadCounter(it->get(), &counter)) {
730 return false;
731 }
732 selection.hotplugged_counters.push_back(counter);
733 } else {
734 if ((*it)->HasMappedBuffer()) {
735 if (!(*it)->StopPolling()) {
736 return false;
737 }
738 }
739 }
740 it = selection.event_fds.erase(it);
741 } else {
742 ++it;
743 }
744 }
745 }
746 }
747 return true;
748 }
749
HandleCpuOnlineEvent(int cpu)750 bool EventSelectionSet::HandleCpuOnlineEvent(int cpu) {
751 // We need to start profiling when opening new event files.
752 SetEnableOnExec(false);
753 std::map<pid_t, std::set<pid_t>> process_map = PrepareThreads(processes_, threads_);
754 for (auto& group : groups_) {
755 if (IsUserSpaceSamplerGroup(group)) {
756 continue;
757 }
758 for (const auto& pair : process_map) {
759 for (const auto& tid : pair.second) {
760 std::string failed_event_type;
761 if (!OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
762 // If failed to open event files, maybe the cpu has been offlined.
763 PLOG(WARNING) << "failed to open perf event file for event_type "
764 << failed_event_type << " for "
765 << (tid == -1 ? "all threads" : "thread " + std::to_string(tid))
766 << " on cpu " << cpu;
767 }
768 }
769 }
770 }
771 if (!for_stat_cmd_) {
772 // Prepare mapped buffer.
773 if (!CreateMappedBufferForCpu(cpu)) {
774 return false;
775 }
776 // Send a EventIdRecord.
777 std::vector<uint64_t> event_id_data;
778 uint64_t attr_id = 0;
779 for (const auto& group : groups_) {
780 for (const auto& selection : group) {
781 for (const auto& event_fd : selection.event_fds) {
782 if (event_fd->Cpu() == cpu) {
783 event_id_data.push_back(attr_id);
784 event_id_data.push_back(event_fd->Id());
785 }
786 }
787 ++attr_id;
788 }
789 }
790 EventIdRecord r(event_id_data);
791 if (!record_callback_(&r)) {
792 return false;
793 }
794 }
795 return true;
796 }
797
CreateMappedBufferForCpu(int cpu)798 bool EventSelectionSet::CreateMappedBufferForCpu(int cpu) {
799 EventFd* fd_with_buffer = nullptr;
800 for (auto& group : groups_) {
801 for (auto& selection : group) {
802 for (auto& event_fd : selection.event_fds) {
803 if (event_fd->Cpu() != cpu) {
804 continue;
805 }
806 if (fd_with_buffer == nullptr) {
807 if (!event_fd->CreateMappedBuffer(mmap_pages_, true)) {
808 return false;
809 }
810 fd_with_buffer = event_fd.get();
811 } else {
812 if (!event_fd->ShareMappedBuffer(*fd_with_buffer, true)) {
813 fd_with_buffer->DestroyMappedBuffer();
814 return false;
815 }
816 }
817 }
818 }
819 }
820 if (fd_with_buffer != nullptr &&
821 !fd_with_buffer->StartPolling(*loop_, [this]() {
822 return ReadMmapEventData();
823 })) {
824 return false;
825 }
826 return true;
827 }
828
StopWhenNoMoreTargets(double check_interval_in_sec)829 bool EventSelectionSet::StopWhenNoMoreTargets(double check_interval_in_sec) {
830 return loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec),
831 [&]() { return CheckMonitoredTargets(); });
832 }
833
CheckMonitoredTargets()834 bool EventSelectionSet::CheckMonitoredTargets() {
835 if (!HasSampler()) {
836 return loop_->ExitLoop();
837 }
838 for (const auto& tid : threads_) {
839 if (IsThreadAlive(tid)) {
840 return true;
841 }
842 }
843 for (const auto& pid : processes_) {
844 if (IsThreadAlive(pid)) {
845 return true;
846 }
847 }
848 return loop_->ExitLoop();
849 }
850
HasSampler()851 bool EventSelectionSet::HasSampler() {
852 for (auto& group : groups_) {
853 for (auto& sel : group) {
854 if (!sel.event_fds.empty()) {
855 return true;
856 }
857 for (auto& sampler : sel.inplace_samplers) {
858 if (!sampler->IsClosed()) {
859 return true;
860 }
861 }
862 }
863 }
864 return false;
865 }
866