Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
LibUnwindStackTraceService.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2024 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/* LibUnwindStackTraceService.cc (C) 2000-2024 */
9/* */
10/* Service de trace des appels de fonctions utilisant 'libunwind'. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/PlatformUtils.h"
15#include "arcane/utils/IStackTraceService.h"
16#include "arcane/utils/StackTrace.h"
17#include "arcane/utils/StringBuilder.h"
18#include "arcane/utils/Array.h"
19#include "arcane/utils/Process.h"
20
21#include "arcane/utils/ISymbolizerService.h"
22#include "arcane/ServiceBuilder.h"
23#include "arcane/Directory.h"
24
25#include "arcane/ServiceFactory.h"
26#include "arcane/AbstractService.h"
27
28#define UNW_LOCAL_ONLY
29#include <libunwind.h>
30#include <stdio.h>
31#include <cxxabi.h>
32
33#include <map>
34#include <mutex>
35
36#include <execinfo.h>
37#include <stdio.h>
38#include <sys/types.h>
39#include <unistd.h>
40#include <stdlib.h>
41#include <dlfcn.h>
42
43/*---------------------------------------------------------------------------*/
44/*---------------------------------------------------------------------------*/
45
46namespace Arcane
47{
48
49/*---------------------------------------------------------------------------*/
50/*---------------------------------------------------------------------------*/
55: public AbstractService
56, public IStackTraceService
57{
58 private:
59 struct ProcInfo
60 {
61 public:
62 ProcInfo() = default;
63 explicit ProcInfo(const String& aname) : m_name(aname){}
64 public:
65 const String& name() const { return m_name; }
67 const char* fileName() const { return m_file_name; }
68 public:
72 const char* m_file_name = nullptr;
74 //TODO: ne pas stocker cela pour chaque fonction.
76 unw_word_t m_base_ip = 0;
77 };
78 public:
79
81 : AbstractService(sbi), m_want_gdb_info(false), m_use_backtrace(false)
82 {
83 m_application = sbi.application();
84 }
85
86 public:
87
88 void build() override
89 {
90 if (!platform::getEnvironmentVariable("ARCANE_GDB_STACK").null())
91 m_want_gdb_info = true;
92 if (!platform::getEnvironmentVariable("ARCANE_USE_BACKTRACE").null())
93 m_use_backtrace = true;
94 }
95
96 public:
97
101
102 private:
103
104 using ProcInfoMap = std::map<unw_word_t,ProcInfo>;
105 ProcInfoMap m_proc_name_map;
106 std::mutex m_proc_name_map_mutex;
107
108 bool m_want_gdb_info;
109 bool m_use_backtrace;
110 IApplication* m_application; //A SUPPRIMER
111 ProcInfo _getFuncInfo(unw_word_t ip,unw_cursor_t* cursor);
112 ProcInfo _getFuncInfo(void* ip);
113 String _getGDBStack();
117};
118
119/*---------------------------------------------------------------------------*/
120/*---------------------------------------------------------------------------*/
121
122LibUnwindStackTraceService::ProcInfo LibUnwindStackTraceService::
123_getFuncInfo(unw_word_t ip,unw_cursor_t* cursor)
124{
125 {
126 std::lock_guard<std::mutex> lk(m_proc_name_map_mutex);
127 auto v = m_proc_name_map.find(ip);
128 if (v!=m_proc_name_map.end())
129 return v->second;
130 }
131
132 unw_word_t offset;
133 char func_name_buf[10000];
134 char demangled_func_name_buf[10000];
135 unw_get_proc_name(cursor,func_name_buf,10000,&offset);
136 int dstatus = 0;
137 size_t len = 10000;
138 char* buf = abi::__cxa_demangle (func_name_buf,demangled_func_name_buf,&len,&dstatus);
139 ProcInfo pi;
140 pi.m_base_ip = offset;
141 {
142 Dl_info dl_info;
143 void* addr = (void*)ip;
144 int r2 = dladdr(addr,&dl_info);
145 if (r2!=0){
146 const char* dli_fname = dl_info.dli_fname;
147 // Adresse de base de chargement du fichier.
148 void* dli_fbase = dl_info.dli_fbase;
149 pi.m_file_name = dli_fname;
150 pi.m_file_loaded_address = (unw_word_t)dli_fbase;
151 }
152 }
153
154 if (buf)
155 pi.m_name = std::string_view(buf);
156 else
157 pi.m_name = std::string_view(func_name_buf);
158 {
159 std::lock_guard<std::mutex> lk(m_proc_name_map_mutex);
160 m_proc_name_map.insert(ProcInfoMap::value_type(ip,pi));
161 }
162 return pi;
163}
164
165/*---------------------------------------------------------------------------*/
166/*---------------------------------------------------------------------------*/
171LibUnwindStackTraceService::ProcInfo LibUnwindStackTraceService::
172_getFuncInfo(void* addr)
173{
174 {
175 std::lock_guard<std::mutex> lk(m_proc_name_map_mutex);
176 auto v = m_proc_name_map.find((unw_word_t)addr);
177 if (v!=m_proc_name_map.end()){
178 return v->second;
179 }
180 }
181 const size_t buf_size = 10000;
184 int r = dladdr(addr,&dl_info);
185 if (r==0){
186 // Erreur dans dladdr.
187 std::cout << "ERROR in dladdr\n";
188 return ProcInfo("Unknown");
189 }
190 const char* dli_sname = dl_info.dli_sname;
191 if (!dli_sname)
192 dli_sname = "";
193 int dstatus = 0;
194 size_t len = buf_size;
195 char* buf = abi::__cxa_demangle (dli_sname,demangled_func_name_buf,&len,&dstatus);
196 //char* buf = (char*)dli_sname;
197 ProcInfo pi;
198 if (buf)
199 pi.m_name = std::string_view(buf);
200 else
201 pi.m_name = std::string_view(dli_sname);
202 {
203 std::lock_guard<std::mutex> lk(m_proc_name_map_mutex);
204 m_proc_name_map.insert(ProcInfoMap::value_type((unw_word_t)addr,pi));
205 }
206 return pi;
207}
208
209/*---------------------------------------------------------------------------*/
210/*---------------------------------------------------------------------------*/
211
212String LibUnwindStackTraceService::
213_getGDBStack()
214{
215 void *array [256];
216 char **names;
217 int i, size;
218
219 fprintf (stderr, "\nNative stacktrace:\n\n");
220
221 size = backtrace (array, 256);
222 names = backtrace_symbols (array, size);
223 for (i =0; i < size; ++i) {
224 fprintf (stderr, "\t%s\n", names [i]);
225 }
226
227 fflush (stderr);
228 return platform::getGDBStack();
229}
230
231/*---------------------------------------------------------------------------*/
232/*---------------------------------------------------------------------------*/
233
237{
240
242 if (m_want_gdb_info)
243 last_str = _getGDBStack();
245 if (m_use_backtrace)
247
248 const size_t hexa_buf_size = 100;
249 char hexa[hexa_buf_size+1];
250
253 int current_func = 0;
254 StringBuilder message;
255
257 while (unw_step(&cursor) > 0) {
261 ProcInfo pi = _getFuncInfo(ip,&cursor);
262 String func_name = pi.m_name;
263 message += " ";
264 snprintf(hexa,hexa_buf_size,"%14llx",(long long)ip);
265 message += hexa;
266 message += " ";
267 message += func_name;
268 message += "\n";
269
270 stack_frames.addFrame(StackFrame((intptr_t)ip));
271 }
272 ++current_func;
273 }
275
276 if (ss){
277 // Il faut mieux lire la pile d'appel de _backtrace() car
278 // la libunwind retourne l'adresse de retour pour chaque fonction
279 // ce qui provoque un décalage dans les infos des lignes du code
280 // source (on pointe vers la ligne après celle qu'on est en train d'exécuter).
282 }
283 else{
284 message += "\nFileAndOffsetStack:{{\n";
286 message += "}}\n";
287 }
288 message += last_str;
289 return StackTrace(stack_frames,message);
290}
291
292/*---------------------------------------------------------------------------*/
293/*---------------------------------------------------------------------------*/
301{
305 //unw_word_t offset;
306
308 if (m_want_gdb_info)
309 last_str = _getGDBStack();
310
313 int current_func = 0;
314 StringBuilder message;
315
316 while (unw_step(&cursor) > 0) {
319 ProcInfo pi = _getFuncInfo(ip,&cursor);
320 String func_name = pi.m_name;
321 message += func_name;
322 break;
323 }
324 ++current_func;
325 }
326 return StackTrace(message.toString()+last_str);
327}
328
329/*---------------------------------------------------------------------------*/
330/*---------------------------------------------------------------------------*/
331
335{
336 void *ips [256];
337 Integer size = backtrace (ips, 256);
338
340 for( Integer i=first_function; i<size; ++i ){
341 stack_frames.addFrame(StackFrame((intptr_t)ips[i]));
342 }
343 return stack_frames;
344}
345
346/*---------------------------------------------------------------------------*/
347/*---------------------------------------------------------------------------*/
348
352{
353 const size_t buf_size = 100;
354 char hexa[buf_size+1];
355
356 StringBuilder message;
358 for( StackFrame f : frames_view ){
359 intptr_t ip = f.address();
360 ProcInfo pinfo = _getFuncInfo((void*)ip);
361 String func_name = pinfo.name();
362 message += " ";
363 snprintf(hexa,buf_size,"%10llx",(long long)ip);
364 message += hexa;
365 message += " ";
366 message += func_name;
367 message += "\n";
368 }
369 return StackTrace(stack_frames,message);
370}
371
372/*---------------------------------------------------------------------------*/
373/*---------------------------------------------------------------------------*/
386{
387 const size_t buf_size = 100;
388 char hexa[buf_size+1];
389
390 StringBuilder message;
392 for( StackFrame f : frames_view ){
393 intptr_t ip = f.address();
394 ProcInfo pinfo = _getFuncInfo((void*)ip);
395 message += (pinfo.fileName() ? pinfo.fileName() : "()");
396 message += " ";
397 intptr_t file_base_address = pinfo.m_file_loaded_address;
398 // Sous Linux (CentOS 6 et 7), l'adresse 0x400000 correspond à celle
399 // de chargement de l'exécutable mais si on retranche cette adresse de celle
400 // de la fonction alors le symboliser ne fonctionne pas (que ce soit
401 // llvm-symbolize ou addr2line)
402 if (file_base_address==0x400000)
404 intptr_t offset_ip = (ip - file_base_address);
405 snprintf(hexa,buf_size,"%llx",(long long)offset_ip);
406 message += "0x";
407 message += hexa;
408 message += "\n";
409 }
410 return message;
411}
412
413/*---------------------------------------------------------------------------*/
414/*---------------------------------------------------------------------------*/
415
416/*---------------------------------------------------------------------------*/
417/*---------------------------------------------------------------------------*/
418
420: public AbstractService
421, public ISymbolizerService
422{
423 public:
425 : AbstractService(sbi), m_is_check_done(false), m_is_valid(false) {}
426 virtual ~LLVMSymbolizerService() {}
427 public:
428 void build() override
429 {
430 m_llvm_symbolizer_path = platform::getEnvironmentVariable("ARCANE_LLVMSYMBOLIZER_PATH");
431 }
432 public:
434 private:
435 String m_llvm_symbolizer_path;
436 bool m_is_check_done;
437 bool m_is_valid;
440 {
441 if (m_is_check_done)
442 return;
443 // Avant appel à cette méthode, m_llvm_symbolizer_path doit contenir
444 // le nom du répertoire dans lequel se trouve llvm-symbolizer.
445 Directory dir(m_llvm_symbolizer_path);
446 String fullpath = dir.file("llvm-symbolizer");
447 Int64 length = platform::getFileLength(fullpath);
448 m_llvm_symbolizer_path = fullpath;
449 if (length>0)
450 m_is_valid = true;
451 m_is_check_done = true;
452 }
453};
454
455/*---------------------------------------------------------------------------*/
456/*---------------------------------------------------------------------------*/
457
460{
461 _checkValid();
462 if (!m_is_valid)
463 return String();
464 std::stringstream ostr;
465 // NOTE: le code ci dessous est similaire à
466 // LibUnwindStackTraceService::_generateFileAndOffset(). Il faudrait
467 // fusionner les deux.
468 for( Integer i=0, n=frames.size(); i<n; ++i ){
470 intptr_t addr = frames[i].address();
471 int r2 = dladdr((void*)addr,&dl_info);
472 const char* dli_fname = nullptr;
473 intptr_t base_address = 0;
474 if (r2!=0){
475 dli_fname = dl_info.dli_fname;
476 // Adresse de base de chargement du fichier.
477 void* dli_fbase = dl_info.dli_fbase;
478 intptr_t true_base = (intptr_t)dli_fbase;
479 // Sous Linux (CentOS 6 et 7), l'adresse 0x400000 correspond à celle
480 // de chargement de l'exécutable mais si on retranche cette adresse de celle
481 // de la fonction alors le symboliser ne fonctionne pas (que ce soit
482 // llvm-symbolize ou addr2line)
483 if (true_base==0x400000)
484 true_base = 0;
486 }
487 // TODO: écrire base address en hexa pour pouvoir le relire avec addr2line
488 ostr << (dli_fname ? dli_fname : "??") << " " << base_address << '\n';
489 }
490
491 std::string input_str(ostr.str());
493 ProcessExecArgs args;
494 args.setCommand(m_llvm_symbolizer_path);
495 Integer input_size = arcaneCheckArraySize(input_str.length());
497 args.setInputBytes(input_bytes);
498 args.addArguments("--demangle");
499 args.addArguments("--pretty-print");
500 //args.addArguments("--print-source-context-lines=5");
501 Process::execute(args);
502 return String(args.outputBytes());
503}
504
505/*---------------------------------------------------------------------------*/
506/*---------------------------------------------------------------------------*/
507
509 ServiceProperty("LibUnwind",ST_Application),
511
513 ServiceProperty("LLVMSymbolizer",ST_Application),
515
516/*---------------------------------------------------------------------------*/
517/*---------------------------------------------------------------------------*/
518
519} // End namespace Arcane
520
521/*---------------------------------------------------------------------------*/
522/*---------------------------------------------------------------------------*/
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro pour déclarer une interface lors de l'enregistrement d'un service.
Classe de base d'un service.
Classe gérant un répertoire.
Definition Directory.h:33
Interface de l'application.
Interface d'un service de récupération des symboles du code source.
virtual String stackTrace(ConstArrayView< StackFrame > frames)=0
Informations pour la pile d'appel frames.
String stackTrace(ConstArrayView< StackFrame > frames) override
Informations pour la pile d'appel frames.
void _checkValid()
Vérifie que le chemin spécifié est valid.
void build() override
Construction de niveau build du service.
Service de trace des appels de fonctions utilisant la libunwind.
StackTrace _backtraceStackTrace(const FixedStackFrameArray &stack_frames)
Pile d'appel via la fonction backtrace.
FixedStackFrameArray _backtraceStackFrame(int first_function)
Pile d'appel via la fonction backtrace.
String _generateFileAndOffset(const FixedStackFrameArray &stack_frames)
Génère liste des noms de fichier et offset d'une pile d'appel.
void build() override
Construction de niveau build du service.
StackTrace stackTrace(int first_function) override
Chaîne de caractère indiquant la pile d'appel.
StackTrace stackTraceFunction(int function_index) override
Retourne la pile à d'appel actuelle.
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
ByteConstArrayView outputBytes() const
Contient le résultat de la sortie standard (STDOUT) du processus.
Definition Process.h:60
static ProcessExecArgs::ExecStatus execute(ProcessExecArgs &args)
Exécute un processus dont les infos sont contenues dans args.
Definition Process.cc:43
Structure contenant les informations pour créer un service.
Propriétés de création d'un service.
Vue constante d'un tableau de type T.
Conserve une liste de taille fixe maximale de StackFrame.
Interface d'un service de trace des appels de fonctions.
Conserve les adresses correspondantes à une pile d'appel. Cette classe est interne et ne dois pas êtr...
Informations sur la pile d'appel des fonctions.
Constructeur de chaîne de caractère unicode.
String toString() const
Retourne la chaîne de caractères construite.
Chaîne de caractères unicode.
TraceMessage pinfo() const
Flot pour un message d'information en parallèle.
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro pour enregistrer un service.
Integer len(const char *s)
Retourne la longueur de la chaîne s.
ISymbolizerService * getSymbolizerService()
Service utilisé pour obtenir des informations sur les symboles du code source.
String getGDBStack()
Récupère la pile d'appel via gdb.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
Integer arcaneCheckArraySize(unsigned long long size)
Vérifie que size peut être converti dans un 'Integer' pour servir de taille à un tableau....
@ ST_Application
Le service s'utilise au niveau de l'application.
const char * m_file_name
Nom de la bibliothèque (.so ou .exe) dans laquelle se trouve la méthode.
const char * fileName() const
Nom du fichier contenant la procédure. Peut-être nul.
unw_word_t m_file_loaded_address
Adresse de chargement de la bibliothèque.
String m_name
Nom (démanglé) de la procédure.