1*6777b538SAndroid Build Coastguard Worker // Copyright 2022 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "base/android/thread_instruction_count.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
8*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
9*6777b538SAndroid Build Coastguard Worker #include "base/no_destructor.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/threading/thread_local_storage.h"
12*6777b538SAndroid Build Coastguard Worker
13*6777b538SAndroid Build Coastguard Worker #include <linux/perf_event.h>
14*6777b538SAndroid Build Coastguard Worker #include <sys/syscall.h>
15*6777b538SAndroid Build Coastguard Worker #include <unistd.h>
16*6777b538SAndroid Build Coastguard Worker
17*6777b538SAndroid Build Coastguard Worker namespace base {
18*6777b538SAndroid Build Coastguard Worker namespace android {
19*6777b538SAndroid Build Coastguard Worker
20*6777b538SAndroid Build Coastguard Worker namespace {
21*6777b538SAndroid Build Coastguard Worker
22*6777b538SAndroid Build Coastguard Worker constexpr int kPerfFdOpenFailed = -1;
23*6777b538SAndroid Build Coastguard Worker
InstructionCounterFdSlot()24*6777b538SAndroid Build Coastguard Worker ThreadLocalStorage::Slot& InstructionCounterFdSlot() {
25*6777b538SAndroid Build Coastguard Worker static NoDestructor<ThreadLocalStorage::Slot> fd_slot([](void* fd_ptr) {
26*6777b538SAndroid Build Coastguard Worker int fd = checked_cast<int>(reinterpret_cast<intptr_t>(fd_ptr));
27*6777b538SAndroid Build Coastguard Worker if (fd > 0)
28*6777b538SAndroid Build Coastguard Worker close(fd);
29*6777b538SAndroid Build Coastguard Worker });
30*6777b538SAndroid Build Coastguard Worker return *fd_slot;
31*6777b538SAndroid Build Coastguard Worker }
32*6777b538SAndroid Build Coastguard Worker
33*6777b538SAndroid Build Coastguard Worker // Opens a new file descriptor that emits the value of
34*6777b538SAndroid Build Coastguard Worker // PERF_COUNT_HW_INSTRUCTIONS in userspace (excluding kernel and hypervisor
35*6777b538SAndroid Build Coastguard Worker // instructions) for the given |thread_id|, or 0 for the calling thread.
36*6777b538SAndroid Build Coastguard Worker //
37*6777b538SAndroid Build Coastguard Worker // Returns kPerfFdOpenFailed if opening the file descriptor failed.
OpenInstructionCounterFdForThread(int thread_id)38*6777b538SAndroid Build Coastguard Worker int OpenInstructionCounterFdForThread(int thread_id) {
39*6777b538SAndroid Build Coastguard Worker struct perf_event_attr pe = {0};
40*6777b538SAndroid Build Coastguard Worker pe.type = PERF_TYPE_HARDWARE;
41*6777b538SAndroid Build Coastguard Worker pe.size = sizeof(struct perf_event_attr);
42*6777b538SAndroid Build Coastguard Worker pe.config = PERF_COUNT_HW_INSTRUCTIONS;
43*6777b538SAndroid Build Coastguard Worker pe.exclude_kernel = 1;
44*6777b538SAndroid Build Coastguard Worker pe.exclude_hv = 1;
45*6777b538SAndroid Build Coastguard Worker
46*6777b538SAndroid Build Coastguard Worker long fd = syscall(__NR_perf_event_open, &pe, thread_id, /* cpu */ -1,
47*6777b538SAndroid Build Coastguard Worker /* group_fd */ -1, /* flags */ 0);
48*6777b538SAndroid Build Coastguard Worker if (fd < 0) {
49*6777b538SAndroid Build Coastguard Worker PLOG(ERROR) << "perf_event_open: omitting instruction counters";
50*6777b538SAndroid Build Coastguard Worker return kPerfFdOpenFailed;
51*6777b538SAndroid Build Coastguard Worker }
52*6777b538SAndroid Build Coastguard Worker return checked_cast<int>(fd);
53*6777b538SAndroid Build Coastguard Worker }
54*6777b538SAndroid Build Coastguard Worker
55*6777b538SAndroid Build Coastguard Worker // Retrieves the active perf counter FD for the current thread, performing
56*6777b538SAndroid Build Coastguard Worker // lazy-initialization if necessary.
InstructionCounterFdForCurrentThread()57*6777b538SAndroid Build Coastguard Worker int InstructionCounterFdForCurrentThread() {
58*6777b538SAndroid Build Coastguard Worker auto& slot = InstructionCounterFdSlot();
59*6777b538SAndroid Build Coastguard Worker int fd = checked_cast<int>(reinterpret_cast<intptr_t>(slot.Get()));
60*6777b538SAndroid Build Coastguard Worker if (fd == 0) {
61*6777b538SAndroid Build Coastguard Worker fd = OpenInstructionCounterFdForThread(0);
62*6777b538SAndroid Build Coastguard Worker slot.Set(reinterpret_cast<void*>(fd));
63*6777b538SAndroid Build Coastguard Worker }
64*6777b538SAndroid Build Coastguard Worker return fd;
65*6777b538SAndroid Build Coastguard Worker }
66*6777b538SAndroid Build Coastguard Worker
67*6777b538SAndroid Build Coastguard Worker } // namespace
68*6777b538SAndroid Build Coastguard Worker
69*6777b538SAndroid Build Coastguard Worker // static
IsSupported()70*6777b538SAndroid Build Coastguard Worker bool ThreadInstructionCount::IsSupported() {
71*6777b538SAndroid Build Coastguard Worker return InstructionCounterFdForCurrentThread() > 0;
72*6777b538SAndroid Build Coastguard Worker }
73*6777b538SAndroid Build Coastguard Worker
74*6777b538SAndroid Build Coastguard Worker // static
Now()75*6777b538SAndroid Build Coastguard Worker ThreadInstructionCount ThreadInstructionCount::Now() {
76*6777b538SAndroid Build Coastguard Worker DCHECK(IsSupported());
77*6777b538SAndroid Build Coastguard Worker int fd = InstructionCounterFdForCurrentThread();
78*6777b538SAndroid Build Coastguard Worker if (fd <= 0)
79*6777b538SAndroid Build Coastguard Worker return ThreadInstructionCount();
80*6777b538SAndroid Build Coastguard Worker
81*6777b538SAndroid Build Coastguard Worker uint64_t instructions = 0;
82*6777b538SAndroid Build Coastguard Worker ssize_t bytes_read = read(fd, &instructions, sizeof(instructions));
83*6777b538SAndroid Build Coastguard Worker CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(instructions)))
84*6777b538SAndroid Build Coastguard Worker << "Short reads of small size from kernel memory is not expected. If "
85*6777b538SAndroid Build Coastguard Worker "this fails, use HANDLE_EINTR.";
86*6777b538SAndroid Build Coastguard Worker return ThreadInstructionCount(instructions);
87*6777b538SAndroid Build Coastguard Worker }
88*6777b538SAndroid Build Coastguard Worker
89*6777b538SAndroid Build Coastguard Worker } // namespace android
90*6777b538SAndroid Build Coastguard Worker } // namespace base
91