Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
DbgHelpStackTraceService.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/* DbgHelpStackTraceService.cc (C) 2000-2025 */
9/* */
10/* Function call tracing service using 'DbgHelp'. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/String.h"
15#include "arcane/utils/IStackTraceService.h"
16#include "arcane/utils/ISymbolizerService.h"
17#include "arcane/utils/NotImplementedException.h"
18#include "arcane/utils/StringBuilder.h"
19
20#include "arccore/base/internal/DependencyInjection.h"
21
22#include "arcane/core/AbstractService.h"
24
25#include <Windows.h>
26#include <winnt.h>
27#include <DbgHelp.h>
28#include <string>
29#include <iostream>
30
31// TODO: protect Sym* calls because the methods are not thread-safe.
32// TODO: do not return the full file path but only the
33// last 3 or 4.
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38namespace Arcane
39{
40
41/*---------------------------------------------------------------------------*/
42/*---------------------------------------------------------------------------*/
43
44class ARCANE_STD_EXPORT DbgHelpSymContainer
45{
46 public:
47
48 void init()
49 {
50 _init();
51 }
52
53 FixedStackFrameArray getStackFrames(int first_function)
54 {
56 _init();
57 if (!m_is_good)
58 return frames;
59 _getStack(first_function, frames);
60 return frames;
61 }
62 String getStackSymbols(ConstArrayView<StackFrame> frames)
63 {
64 return _getStackSymbols(frames);
65 }
66
67 private:
68
69 void _init()
70 {
71 if (m_is_init)
72 return;
73 m_is_init = true;
74 ::SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
75 if (!::SymInitialize(::GetCurrentProcess(), nullptr, true)) {
76 m_is_good = false;
77 return;
78 }
79 m_is_good = true;
80 }
81 void _getStack(int first_function, FixedStackFrameArray& frames);
82 String _getStackSymbols(ConstArrayView<StackFrame> frames);
83
84 private:
85
86 bool m_is_init = false;
87 bool m_is_good = false;
88};
89
90/*---------------------------------------------------------------------------*/
91/*---------------------------------------------------------------------------*/
92
93void DbgHelpSymContainer::
94_getStack(int first_function, FixedStackFrameArray& frames)
95{
96 // Adds (+2) to account for the call to this method and the one above it.
97 PVOID addrs[FixedStackFrameArray::MAX_FRAME] = { 0 };
98 USHORT nb_frame = CaptureStackBackTrace(first_function + 2, FixedStackFrameArray::MAX_FRAME, addrs, NULL);
99 for (USHORT i = 0; i < nb_frame; i++) {
100 frames.addFrame(StackFrame((intptr_t)addrs[i]));
101 }
102}
103
104/*---------------------------------------------------------------------------*/
105/*---------------------------------------------------------------------------*/
106
107String DbgHelpSymContainer::
108_getStackSymbols(ConstArrayView<StackFrame> frames)
109{
110 StringBuilder sb;
111 _init();
112 if (!m_is_good)
113 return String();
114 int nb_frame = frames.size();
115 for (USHORT i = 0; i < nb_frame; i++) {
116 DWORD64 addr = (DWORD64)frames[i].address();
117 // Allocate a buffer large enough to hold the symbol information on the stack and get
118 // a pointer to the buffer. We also have to set the size of the symbol structure itself
119 // and the number of bytes reserved for the name.
120 ULONG64 buffer[(sizeof(SYMBOL_INFO) + 1024 + sizeof(ULONG64) - 1) / sizeof(ULONG64)] = { 0 };
121 SYMBOL_INFO* info = (SYMBOL_INFO*)buffer;
122 info->SizeOfStruct = sizeof(SYMBOL_INFO);
123 info->MaxNameLen = 1024;
124
125 // Attempt to get information about the symbol and add it to our output parameter.
126 DWORD64 displacement = 0;
127 if (::SymFromAddr(::GetCurrentProcess(), addr, &displacement, info)) {
128
129 // Attempt to retrieve line number information.
130 DWORD line_displacement = 0;
131 IMAGEHLP_LINE64 line = {};
132 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
133 BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), addr, &line_displacement, &line);
134 sb.append(std::string_view(info->Name, info->NameLen));
135 sb.append("()");
136 if (has_line) {
137 sb.append(" ");
138 sb.append(line.FileName);
139 sb.append(":");
140 sb.append(String::fromNumber(line.LineNumber));
141 }
142 sb.append("\n");
143 }
144 }
145 return sb.toString();
146}
147
148/*---------------------------------------------------------------------------*/
149/*---------------------------------------------------------------------------*/
150
151namespace
152{
153 std::shared_ptr<DbgHelpSymContainer> m_sym_container;
154 std::shared_ptr<DbgHelpSymContainer> _getStaticContainer()
155 {
156 if (!m_sym_container.get())
157 m_sym_container = std::make_shared<DbgHelpSymContainer>();
158 return m_sym_container;
159 }
160} // namespace
161
162/*---------------------------------------------------------------------------*/
163/*---------------------------------------------------------------------------*/
164
165/*---------------------------------------------------------------------------*/
166/*---------------------------------------------------------------------------*/
167
171class DbgHelpStackTraceService
172: public TraceAccessor
173, public IStackTraceService
174{
175 public:
176
177 explicit DbgHelpStackTraceService(const ServiceBuildInfo& sbi)
179 {
180 }
181 explicit DbgHelpStackTraceService(ITraceMng* tm)
182 : TraceAccessor(tm)
183 {
184 }
185
186 public:
187
188 void build() {}
189
190 public:
191
192 StackTrace stackTrace(int first_function) override;
193 StackTrace stackTraceFunction(int function_index) override;
194
195 private:
196
197 std::shared_ptr<DbgHelpSymContainer> m_sym_container;
198 DbgHelpSymContainer* _getContainer()
199 {
200 if (!m_sym_container.get())
201 m_sym_container = _getStaticContainer();
202 return m_sym_container.get();
203 }
204};
205
206/*---------------------------------------------------------------------------*/
207/*---------------------------------------------------------------------------*/
208
210stackTrace(int first_function)
211{
212 DbgHelpSymContainer* c = _getContainer();
213 FixedStackFrameArray frames = c->getStackFrames(first_function);
214 String text = c->getStackSymbols(frames.view());
215 return StackTrace(frames, text);
216}
217
218/*---------------------------------------------------------------------------*/
219/*---------------------------------------------------------------------------*/
220
222stackTraceFunction(int function_index)
223{
224 return StackTrace();
225}
226
227/*---------------------------------------------------------------------------*/
228/*---------------------------------------------------------------------------*/
229
230/*---------------------------------------------------------------------------*/
231/*---------------------------------------------------------------------------*/
232
236class DbgHelpSymbolizerService
237: public TraceAccessor
238, public ISymbolizerService
239{
240 public:
241
242 explicit DbgHelpSymbolizerService(const ServiceBuildInfo& sbi)
244 {}
245 explicit DbgHelpSymbolizerService(ITraceMng* tm)
246 : TraceAccessor(tm)
247 {
248 }
249
250 public:
251
252 void build() {}
253
254 public:
255
257 {
258 return _getContainer()->getStackSymbols(frames);
259 }
260
261 private:
262
263 std::shared_ptr<DbgHelpSymContainer> m_sym_container;
264 DbgHelpSymContainer* _getContainer()
265 {
266 if (!m_sym_container.get())
267 m_sym_container = _getStaticContainer();
268 return m_sym_container.get();
269 }
270};
271
272/*---------------------------------------------------------------------------*/
273/*---------------------------------------------------------------------------*/
274
276 ServiceProperty("DbgHelpStackTraceService", ST_Application),
278
280 ServiceProperty("DbgHelpSymbolizerService", ST_Application),
282
283ARCANE_DI_REGISTER_PROVIDER(DbgHelpStackTraceService,
284 DependencyInjection::ProviderProperty("DbgHelpStackTraceService"),
285 ARCANE_DI_INTERFACES(IStackTraceService),
286 ARCANE_DI_CONSTRUCTOR(ITraceMng*));
287
288ARCANE_DI_REGISTER_PROVIDER(DbgHelpSymbolizerService,
289 DependencyInjection::ProviderProperty("DbgHelpSymbolizerService"),
290 ARCANE_DI_INTERFACES(ISymbolizerService),
291 ARCANE_DI_CONSTRUCTOR(ITraceMng*));
292
293/*---------------------------------------------------------------------------*/
294/*---------------------------------------------------------------------------*/
295
296} // End namespace Arcane
297
298/*---------------------------------------------------------------------------*/
299/*---------------------------------------------------------------------------*/
This file contains the various service factories and macros for registering services.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro to declare an interface when registering a service.
Constant view of an array of type T.
Function call tracing service using 'DbgHelp'.
StackTrace stackTrace(int first_function) override
Character string indicating the call stack.
StackTrace stackTraceFunction(int function_index) override
Name of a function in the call stack.
Function call tracing service using libunwind.
String stackTrace(ConstArrayView< StackFrame > frames) override
Information for the call stack frames.
Stores a fixed maximum size list of StackFrame.
void addFrame(const StackFrame &frame)
Adds a frame to the list of frames. If nbFrame() is greater than or equal to MAX_FRAME,...
virtual ITraceMng * traceMng() const =0
Trace manager.
Interface of a function call tracing service.
Interface of a source code symbol retrieval service.
IApplication * application() const
Access to the associated IApplication.
Structure containing the information to create a service.
Service creation properties.
Information about function call stacks.
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
#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 --
@ ST_Application
The service is used at the application level.