Arcane  4.2.1.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-2026 */
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#include <cstring>
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane
33{
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38class LinuxPerfPerformanceCounterService
39: public TraceAccessor
41{
42 public:
43
44 explicit LinuxPerfPerformanceCounterService(const ServiceBuildInfo& sbi)
46 {
47 }
48 ~LinuxPerfPerformanceCounterService()
49 {
50 _closeAll();
51 }
52
53 public:
54
55 void build() {}
56
57 void initialize() override
58 {
59 _checkInitialize();
60 }
61
63 bool _addEvent(int event_type, int event_config, bool is_optional = false)
64 {
65 struct perf_event_attr attr;
66 std::memset(&attr, 0, sizeof(attr));
67
68 attr.type = event_type,
69 attr.config = event_config;
70 attr.size = sizeof(struct perf_event_attr);
71 attr.exclude_kernel = 1;
72 attr.exclude_hv = 1;
73 attr.disabled = 1;
74 attr.inherit = 1;
75
76 int cpu = -1;
77 int group_fd = -1;
78 unsigned long flags = 0;
79 long long_fd = syscall(__NR_perf_event_open, &attr, m_process_id, cpu, group_fd, flags);
80 info(4) << "AddEvent type=" << attr.type << " id=" << attr.config << " fd=" << long_fd;
81 if (long_fd == (-1)) {
82 if (is_optional)
83 return true;
84 ARCANE_FATAL("ERROR for event type={0} id={1} error={2}", attr.type, attr.config, std::strerror(errno));
85 }
86 int fd = static_cast<int>(long_fd);
87 m_events_file_descriptor.add(fd);
88 return false;
89 }
90
91 void start() override
92 {
93 if (m_is_started)
94 ARCANE_FATAL("start() has alredy been called");
95 _checkInitialize();
96 info(4) << "LinuxPerf: Start";
97 for (int fd : m_events_file_descriptor) {
98 int r = ::ioctl(fd, PERF_EVENT_IOC_ENABLE);
99 if (r != 0)
100 ARCANE_FATAL("Error starting event r={0} error={1}", r, strerror(errno));
101 }
102 m_is_started = true;
103 }
104 void stop() override
105 {
106 if (!m_is_started)
107 ARCANE_FATAL("start() has not been called");
108 info(4) << "LinuxPerf: Stop";
109 for (int fd : m_events_file_descriptor) {
110 int r = ::ioctl(fd, PERF_EVENT_IOC_DISABLE);
111 if (r != 0)
112 ARCANE_FATAL("Error stopping event r={0} error={1}", r, strerror(errno));
113 }
114 m_is_started = false;
115 }
116 bool isStarted() const override
117 {
118 return m_is_started;
119 }
120
121 Integer getCounters(Int64ArrayView counters, bool do_substract) override
122 {
123 Int32 index = 0;
124 for (Int32 index = 0, n = m_events_file_descriptor.size(); index < n; ++index) {
125 Int64 value = _getOneCounter(index);
126 Int64 current_value = counters[index];
127 counters[index] = (do_substract) ? value - current_value : value;
128 }
129 return index;
130 }
131
132 Int64 getCycles() override
133 {
134 return _getOneCounter(0);
135 }
136
137 private:
138
139 UniqueArray<int> m_events_file_descriptor;
140 bool m_is_started = false;
141 bool m_is_init = false;
142 pid_t m_process_id = -1;
143
144 private:
145
146 void _closeAll()
147 {
148 for (int fd : m_events_file_descriptor) {
149 if (fd >= 0)
150 ::close(fd);
151 }
152 m_events_file_descriptor.clear();
153 }
154
155 Int64 _getOneCounter(Int32 index)
156 {
157 int fd = m_events_file_descriptor[index];
158 uint64_t value[1] = { 0 };
159 const size_t s = sizeof(uint64_t);
160 size_t nb_read = ::read(fd, value, s);
161 if (nb_read != s)
162 ARCANE_FATAL("Can not read counter index={0}", index);
163 return value[0];
164 }
165
166 void _checkInitialize()
167 {
168 if (m_is_init)
169 return;
170
171 info() << "Initialize LinuxPerfPerformanceCounterService";
172 m_process_id = ::getpid();
173 // CPU cycle count. This counter is essential.
174 _addEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
175 // Number of instructions executed. This counter is essential.
176 _addEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS);
177
178 // The following counters are not essential and it is not
179 // always easy to know which ones are available for a
180 // given platform. We try them in the following order:
181 // 1. Last level cache miss count (generally L3 cache)
182 // 2. Number of cycles where the CPU is waiting for something.
183 // 3. Cache miss count (presumably for all caches)
184 const bool is_optional = true;
185 bool is_bad = true;
186 {
187 int cache_access = (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16);
188 is_bad = _addEvent(PERF_TYPE_HW_CACHE, PERF_COUNT_HW_CACHE_LL | cache_access, is_optional);
189 }
190 if (is_bad)
191 is_bad = _addEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, is_optional);
192 if (is_bad)
193 _addEvent(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, is_optional);
194
195 m_is_init = true;
196 }
197};
198
199/*---------------------------------------------------------------------------*/
200/*---------------------------------------------------------------------------*/
201
203 ServiceProperty("LinuxPerfPerformanceCounterService", ST_Application),
205
206/*---------------------------------------------------------------------------*/
207/*---------------------------------------------------------------------------*/
208
209} // End namespace Arcane
210
211/*---------------------------------------------------------------------------*/
212/*---------------------------------------------------------------------------*/
#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:445
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.