xref: /aosp_15_r20/external/cronet/base/android/thread_instruction_count.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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