Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
LinuxPerfPerformanceCounterService.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* LinuxPerfPerformanceCounterService.cc (C) 2000-2022 */
9/* */
10/* Hardware counter retrieval via the Linux 'perf' API. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/IPerformanceCounterService.h"
15#include "arcane/utils/TraceAccessor.h"
16#include "arcane/utils/FatalErrorException.h"
17
18#include "arcane/core/FactoryService.h"
19
20#include <linux/perf_event.h>
21#include <linux/hw_breakpoint.h>
22#include <sys/types.h>
23#include <unistd.h>
24#include <syscall.h>
25#include <sys/ioctl.h>
26
27/*---------------------------------------------------------------------------*/
28/*---------------------------------------------------------------------------*/
29
30namespace Arcane
31{
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35
36class LinuxPerfPerformanceCounterService
37: public TraceAccessor
39{
40 public:
41
42 explicit LinuxPerfPerformanceCounterService(const ServiceBuildInfo& sbi)
44 {
45 }
46 ~LinuxPerfPerformanceCounterService()
47 {
48 _closeAll();
49 }
50
51 public:
52
53 void build() {}
54
55 void initialize() override
56 {
57 _checkInitialize();
58 }
59
61 bool _addEvent(int event_type, int event_config, bool is_optional = false)
62 {
63 struct perf_event_attr attr;
64 memset(&attr, 0, sizeof(attr));
65
66 attr.type = event_type,
67 attr.config = event_config;
68 attr.size = sizeof(struct perf_event_attr);
69 attr.exclude_kernel = 1;
70 attr.exclude_hv = 1;
71 attr.disabled = 1;
72 attr.inherit = 1;
73
74 int cpu = -1;
75 int group_fd = -1;
76 unsigned long flags = 0;
77 long long_fd = syscall(__NR_perf_event_open, &attr, m_process_id, cpu, group_fd, flags);
78 info(4) << "AddEvent type=" << attr.type << " id=" << attr.config << " fd=" << long_fd;
79 if (long_fd == (-1)) {
80 if (is_optional)
81 return true;
82 ARCANE_FATAL("ERROR for event type={0} id={1} error={2}", attr.type, attr.config, strerror(errno));
83 }
84 int fd = static_cast<int>(long_fd);
85 m_events_file_descriptor.add(fd);
86 return false;
87 }
88
89 void start() override
90 {
91 if (m_is_started)
92 ARCANE_FATAL("start() has alredy been called");
93 _checkInitialize();
94 info(4) << "LinuxPerf: Start";
95 for (int fd : m_events_file_descriptor) {
96 int r = ::ioctl(fd, PERF_EVENT_IOC_ENABLE);
97 if (r != 0)
98 ARCANE_FATAL("Error starting event r={0} error={1}", r, strerror(errno));
99 }
100 m_is_started = true;
101 }
102 void stop() override
103 {
104 if (!m_is_started)
105 ARCANE_FATAL("start() has not been called");
106 info(4) << "LinuxPerf: Stop";
107 for (int fd : m_events_file_descriptor) {
108 int r = ::ioctl(fd, PERF_EVENT_IOC_DISABLE);
109 if (r != 0)
110 ARCANE_FATAL("Error stopping event r={0} error={1}", r, strerror(errno));
111 }
112 m_is_started = false;
113 }
114 bool isStarted() const override
115 {
116 return m_is_started;
117 }
118
119 Integer getCounters(Int64ArrayView counters, bool do_substract) override
120 {
121 Int32 index = 0;
122 for (Int32 index = 0, n = m_events_file_descriptor.size(); index < n; ++index) {
123 Int64 value = _getOneCounter(index);
124 Int64 current_value = counters[index];
125 counters[index] = (do_substract) ? value - current_value : value;
126 }
127 return index;
128 }
129
130 Int64 getCycles() override
131 {
132 return _getOneCounter(0);
133 }
134
135 private:
136
137 UniqueArray<int> m_events_file_descriptor;
138 bool m_is_started = false;
139 bool m_is_init = false;
140 pid_t m_process_id = -1;
141
142 private:
143
144 void _closeAll()
145 {
146 for (int fd : m_events_file_descriptor) {
147 if (fd >= 0)
148 ::close(fd);
149 }
150 m_events_file_descriptor.clear();
151 }
152
153 Int64 _getOneCounter(Int32 index)
154 {
155 int fd = m_events_file_descriptor[index];
156 uint64_t value[1] = { 0 };
157 const size_t s = sizeof(uint64_t);
158 size_t nb_read = ::read(fd, value, s);
159 if (nb_read != s)
160 ARCANE_FATAL("Can not read counter index={0}", index);
161 return value[0];
162 }
163
164 void _checkInitialize()
165 {
166 if (m_is_init)
167 return;
168
169 info() << "Initialize LinuxPerfPerformanceCounterService";
170 m_process_id = ::getpid();
171 // CPU cycle count. This counter is essential.
172 _addEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
173 // Number of instructions executed. This counter is essential.
174 _addEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS);
175
176 // The following counters are not essential and it is not
177 // always easy to know which ones are available for a
178 // given platform. We try them in the following order:
179 // 1. Last level cache miss count (generally L3 cache)
180 // 2. Number of cycles where the CPU is waiting for something.
181 // 3. Cache miss count (presumably for all caches)
182 const bool is_optional = true;
183 bool is_bad = true;
184 {
185 int cache_access = (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16);
186 is_bad = _addEvent(PERF_TYPE_HW_CACHE, PERF_COUNT_HW_CACHE_LL | cache_access, is_optional);
187 }
188 if (is_bad)
189 is_bad = _addEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, is_optional);
190 if (is_bad)
191 _addEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, is_optional);
192
193 m_is_init = true;
194 }
195};
196
197/*---------------------------------------------------------------------------*/
198/*---------------------------------------------------------------------------*/
199
201 ServiceProperty("LinuxPerfPerformanceCounterService", ST_Application),
203
204/*---------------------------------------------------------------------------*/
205/*---------------------------------------------------------------------------*/
206
207} // End namespace Arcane
208
209/*---------------------------------------------------------------------------*/
210/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro to declare an interface when registering a service.
void clear()
Removes the elements from the array.
virtual ITraceMng * traceMng() const =0
Trace manager.
Interface of a service for accessing performance counters.
Int64 getCycles() override
Value of the counter for the number of CPU cycles.
void stop() override
Stops tracking performance counters.
bool _addEvent(int event_type, int event_config, bool is_optional=false)
Adds an event and returns true if it fails.
Integer getCounters(Int64ArrayView counters, bool do_substract) override
Retrieves the current values of the counters.
void start() override
Starts tracking performance counters.
bool isStarted() const override
Indicates if the service has started (start() has been called).
IApplication * application() const
Access to the associated IApplication.
Structure containing the information to create a service.
Service creation properties.
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessage info() const
Flow for an information message.
1D data vector with value semantics (STL style).
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro for registering a service.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
ArrayView< Int64 > Int64ArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:451
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
@ ST_Application
The service is used at the application level.
std::int32_t Int32
Signed integer type of 32 bits.