1 // Copyright 2019 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 "base/trace_event/thread_instruction_count.h"
6 
7 #include "base/base_switches.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/no_destructor.h"
11 #include "base/threading/thread_local_storage.h"
12 #include "build/build_config.h"
13 
14 #if defined(OS_LINUX)
15 #include <linux/perf_event.h>
16 #include <sys/syscall.h>
17 #include <unistd.h>
18 #endif  // defined(OS_LINUX)
19 
20 namespace base {
21 namespace trace_event {
22 
23 namespace {
24 
25 #if defined(OS_LINUX)
26 
27 // Special constants used for counter FD states.
28 constexpr int kPerfFdDisabled = -2;
29 constexpr int kPerfFdOpenFailed = -1;
30 constexpr int kPerfFdUninitialized = 0;
31 
InstructionCounterFdSlot()32 ThreadLocalStorage::Slot& InstructionCounterFdSlot() {
33   static NoDestructor<ThreadLocalStorage::Slot> fd_slot([](void* fd_ptr) {
34     int fd = reinterpret_cast<intptr_t>(fd_ptr);
35     if (fd > kPerfFdUninitialized)
36       close(fd);
37   });
38   return *fd_slot;
39 }
40 
41 // Opens a new file descriptor that emits the value of
42 // PERF_COUNT_HW_INSTRUCTIONS in userspace (excluding kernel and hypervisor
43 // instructions) for the given |thread_id|, or 0 for the calling thread.
44 //
45 // Returns kPerfFdOpenFailed if opening the file descriptor failed, or
46 // kPerfFdDisabled if performance counters are disabled in the calling process.
OpenInstructionCounterFdForThread(int thread_id)47 int OpenInstructionCounterFdForThread(int thread_id) {
48   // This switch is only propagated for processes that are unaffected by the
49   // BPF sandbox, such as the browser process or renderers with --no-sandbox.
50   const base::CommandLine& command_line =
51       *base::CommandLine::ForCurrentProcess();
52   if (!command_line.HasSwitch(switches::kEnableThreadInstructionCount))
53     return kPerfFdDisabled;
54 
55   struct perf_event_attr pe = {0};
56   pe.type = PERF_TYPE_HARDWARE;
57   pe.size = sizeof(struct perf_event_attr);
58   pe.config = PERF_COUNT_HW_INSTRUCTIONS;
59   pe.exclude_kernel = 1;
60   pe.exclude_hv = 1;
61 
62   int fd = syscall(__NR_perf_event_open, &pe, thread_id, /* cpu */ -1,
63                    /* group_fd */ -1, /* flags */ 0);
64   if (fd < 0) {
65     LOG(ERROR) << "perf_event_open failed, omitting instruction counters";
66     return kPerfFdOpenFailed;
67   }
68   return fd;
69 }
70 
71 // Retrieves the active perf counter FD for the current thread, performing
72 // lazy-initialization if necessary.
InstructionCounterFdForCurrentThread()73 int InstructionCounterFdForCurrentThread() {
74   auto& slot = InstructionCounterFdSlot();
75   int fd = reinterpret_cast<intptr_t>(slot.Get());
76   if (fd == kPerfFdUninitialized) {
77     fd = OpenInstructionCounterFdForThread(0);
78     slot.Set(reinterpret_cast<void*>(fd));
79   }
80   return fd;
81 }
82 
83 #endif  // defined(OS_LINUX)
84 
85 }  // namespace
86 
IsSupported()87 bool ThreadInstructionCount::IsSupported() {
88 #if defined(OS_LINUX)
89   // If we can't initialize the counter FD, mark as disabled.
90   int counter_fd = InstructionCounterFdForCurrentThread();
91   if (counter_fd <= 0)
92     return false;
93 
94   return true;
95 #endif  // defined(OS_LINUX)
96   return false;
97 }
98 
Now()99 ThreadInstructionCount ThreadInstructionCount::Now() {
100   DCHECK(IsSupported());
101 #if defined(OS_LINUX)
102   int fd = InstructionCounterFdForCurrentThread();
103   if (fd <= 0)
104     return ThreadInstructionCount();
105 
106   uint64_t instructions = 0;
107   ssize_t bytes_read = read(fd, &instructions, sizeof(instructions));
108   CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(instructions)))
109       << "Short reads of small size from kernel memory is not expected. If "
110          "this fails, use HANDLE_EINTR.";
111   return ThreadInstructionCount(instructions);
112 #else
113   return ThreadInstructionCount();
114 #endif  // defined(OS_LINUX)
115 }
116 
117 }  // namespace trace_event
118 }  // namespace base
119