Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
ProfilingInfo.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2023 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/* ProfilingInfo.cc (C) 2000-2023 */
9/* */
10/* Informations de profiling. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
15#include "arcane/utils/ArcanePrecomp.h"
16
17#include "arcane/utils/Array.h"
18#include "arcane/utils/String.h"
19#include "arcane/utils/StringBuilder.h"
20#include "arcane/utils/PlatformUtils.h"
21#include "arcane/utils/JSONWriter.h"
22
23#include "arcane/std/ProfilingInfo.h"
24
25#include <set>
26#include <array>
27
28#include "arcane_packages.h"
29
30//#undef ARCANE_HAS_PACKAGE_LIBUNWIND
31
32#ifdef ARCANE_HAS_PACKAGE_LIBUNWIND
33#define UNW_LOCAL_ONLY
34#include <libunwind.h>
35#include <stdio.h>
36#endif
37#ifdef __GNUG__
38#include <cxxabi.h>
39#endif
40
41#if defined(ARCANE_OS_LINUX)
42#include <execinfo.h>
43#include <dlfcn.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <unistd.h>
47
48#endif
49
50#if defined(ARCANE_OS_LINUX)
51#define ARCANE_HAS_GLIBC_BACKTRACE
52#endif
53
54#ifdef ARCANE_USE_MALLOC_HOOK
55#include <malloc.h>
56#endif
57
58#include <atomic>
59#include <algorithm>
60
61/*---------------------------------------------------------------------------*/
62/*---------------------------------------------------------------------------*/
63
64namespace Arcane
65{
66
67/*---------------------------------------------------------------------------*/
68/*---------------------------------------------------------------------------*/
75{
76 public:
77 virtual ~IStackInfoProvider(){}
78 virtual Integer nbIndex() const =0;
79 virtual intptr_t functionStartAddress(Int32 stack_index) =0;
80 virtual void fillStack(Integer function_depth) =0;
81 virtual void setFunc(Int32 func_index,Int32 stack_index) =0;
82};
83
84/*---------------------------------------------------------------------------*/
85/*---------------------------------------------------------------------------*/
86
88{
89 public:
90 virtual ~IFuncInfoProvider(){}
91 virtual void fillFuncName(ProfFuncInfo& pfi) =0;
92};
93
94/*---------------------------------------------------------------------------*/
95/*---------------------------------------------------------------------------*/
96
98: public IFuncInfoProvider
99{
100 public:
101 virtual ~NullFuncInfoProvider(){}
102 virtual void fillFuncName(ProfFuncInfo& pfi) override
103 {
104 pfi.m_func_name[0] = '\0';
105 pfi.setHasFuncName(true);
106 }
107};
108
109/*---------------------------------------------------------------------------*/
110/*---------------------------------------------------------------------------*/
111
112#ifdef ARCANE_HAS_PACKAGE_LIBUNWIND
113class ProfInfos::LibUnwindFuncInfos
114: public IFuncInfoProvider
115{
116 public:
117 void fillFuncName(ProfFuncInfo& pfi) override
118 {
119 Int32 func_index = pfi.index();
121 unw_word_t offset;
122 unw_get_proc_name(cursor,pfi.m_func_name,MAX_FUNC_LEN,&offset);
123 pfi.setHasFuncName(true);
124 }
125 void setFunc(Int32 func_index,const unw_cursor_t& cursor)
126 {
128 }
129 private:
130 unw_cursor_t func_cursor[MAX_FUNC];
131};
132
133class ProfInfos::LibUnwindStackInfo
134: public IStackInfoProvider
135{
136 public:
137 LibUnwindStackInfo(LibUnwindFuncInfos* func_infos)
138 : m_nb_index(0), m_func_infos(func_infos){}
139 public:
140 void fillStack(Integer function_depth) override
141 {
142 Integer index = 0;
143 unw_context_t uc;
144 unw_cursor_t cursor;
145 unw_getcontext(&uc);
146 unw_init_local(&cursor, &uc);
147 unw_proc_info_t proc_info;
148 while (unw_step(&cursor) > 0 && index<(MAX_STACK+function_depth)) {
149 unw_get_proc_info(&cursor,&proc_info);
150 m_cursors[index] = cursor;
151 m_proc_start[index] = proc_info.start_ip;
152 ++index;
153 }
154 m_nb_index = index;
155 }
156 void setFunc(Int32 func_index,Int32 stack_index) override
157 {
158 m_func_infos->setFunc(func_index,m_cursors[stack_index]);
159 }
160 public:
161 Integer nbIndex() const override { return m_nb_index; }
162 intptr_t functionStartAddress(Int32 stack_index) override
163 {
164 return (intptr_t)m_proc_start[stack_index];
165 }
166 private:
167 // TODO: vérifier taille
168 unw_cursor_t m_cursors[256];
169 unw_word_t m_proc_start[256];
170 Integer m_nb_index;
171 LibUnwindFuncInfos* m_func_infos;
172};
173#endif
174
175/*---------------------------------------------------------------------------*/
176/*---------------------------------------------------------------------------*/
177
178#ifdef ARCANE_HAS_GLIBC_BACKTRACE
179class ProfInfos::BacktraceFuncInfos
180: public IFuncInfoProvider
181{
182 public:
183 void fillFuncName(ProfFuncInfo& pfi) override
184 {
185 Int32 func_index = pfi.index();
186 int copy_index = 0;
187 const char* func_name = func_dl_info[func_index].dli_sname;
188 if (func_name){
189 for( ; copy_index<MAX_FUNC_LEN; ++copy_index ){
190 char c = func_name[copy_index];
191 pfi.m_func_name[copy_index] = c;
192 if (c=='\0')
193 break;
194 }
195 }
196 pfi.m_func_name[copy_index] = '\0';
197 pfi.setHasFuncName(true);
198 }
199 void setFunc(Int32 func_index,const Dl_info& dl_info)
200 {
201 func_dl_info[func_index] = dl_info;
202 }
203 private:
204 Dl_info func_dl_info[MAX_FUNC];
205};
206class ProfInfos::BacktraceStackInfo
207: public IStackInfoProvider
208{
209 public:
210 BacktraceStackInfo(BacktraceFuncInfos* func_infos)
211 : m_nb_index(0), m_func_infos(func_infos){}
212 public:
213 void fillStack(Integer function_depth) override
214 {
215 ARCANE_UNUSED(function_depth);
216 void* addrs[64];
217 m_nb_index = backtrace(addrs,64);
218 for( Integer index=0; index<m_nb_index; ++index ){
219 int err_code = dladdr(addrs[index],&m_dl_infos[index]);
220 if (err_code!=0)
221 m_proc_start[index] = (intptr_t)m_dl_infos[index].dli_saddr;
222 else
223 m_proc_start[index] = 0;
224 }
225 }
226 void setFunc(Int32 func_index,Int32 stack_index) override
227 {
228 m_func_infos->setFunc(func_index,m_dl_infos[stack_index]);
229 }
230 public:
231 Integer nbIndex() const override { return m_nb_index; }
232 intptr_t functionStartAddress(Int32 stack_index) override
233 {
234 return (intptr_t)m_proc_start[stack_index];
235 }
236 private:
237 // TODO: vérifier taille
238 std::array<Dl_info,256> m_dl_infos = { };
239 std::array<intptr_t,256> m_proc_start = { };
240 Integer m_nb_index;
241 BacktraceFuncInfos* m_func_infos;
242};
243#endif
244
245/*---------------------------------------------------------------------------*/
246/*---------------------------------------------------------------------------*/
247
249{
250 private:
251 struct _MonoJitInfo;
252 struct _MonoDomain;
253 struct _MonoMethod;
254
255 typedef void* (*mono_jit_info_get_code_start_func)(_MonoJitInfo* ji);
256 typedef _MonoDomain* (*mono_domain_get_func)();
257 typedef _MonoJitInfo* (*mono_jit_info_table_find_func)(_MonoDomain*,void* ip);
258 typedef char* (*mono_pmip_func)(void* ip);
259 typedef _MonoMethod* (*mono_jit_info_get_method_func)(_MonoJitInfo* ji);
260 typedef char* (*mono_method_full_name_func)(_MonoMethod* method,bool full);
261 public:
262 MonoFuncAddrGetter() : m_is_valid(false), m_handle(0)
263 {
264 empty_func_name[0] = '\0';
265#if defined(ARCANE_OS_LINUX)
266 void* handle = dlopen (0, RTLD_LAZY);
267 if (!handle)
268 return;
269 m_handle = handle;
270 m_mono_jit_info_get_code_start_ptr = (mono_jit_info_get_code_start_func)(dlsym(handle,"mono_jit_info_get_code_start"));
271 m_mono_domain_get_ptr = (mono_domain_get_func)(dlsym(handle,"mono_domain_get"));
272 m_mono_jit_info_table_find_ptr = (mono_jit_info_table_find_func)(dlsym(handle,"mono_jit_info_table_find"));
273 m_mono_pmip_ptr = (mono_pmip_func)(dlsym(handle,"mono_pmip"));
274 m_mono_jit_info_get_method_ptr = (mono_jit_info_get_method_func)(dlsym(handle,"mono_jit_info_get_method"));
275 m_mono_method_full_name_ptr = (mono_method_full_name_func)(dlsym(handle,"mono_method_full_name"));
276
277 if (!m_mono_jit_info_get_code_start_ptr)
278 return;
279 if (!m_mono_domain_get_ptr)
280 return;
281 if (!m_mono_jit_info_table_find_ptr)
282 return;
283 if (!m_mono_pmip_ptr)
284 return;
285 if (!m_mono_jit_info_get_method_ptr)
286 return;
287 if (!m_mono_method_full_name_ptr)
288 return;
289 m_is_valid = true;
290#endif
291 }
293 {
294#if defined(ARCANE_OS_LINUX)
295 dlclose(m_handle);
296#endif
297 }
298
299 bool isValid() const
300 {
301 return m_is_valid;
302 }
303 char empty_func_name[1];
304 char* getInfo(void* ip,void** _start_addr)
305 {
306 if (!m_is_valid)
307 return 0;
308 char* func_name = empty_func_name; //(*m_mono_pmip_ptr)(ip);
309 _MonoDomain* d = (*m_mono_domain_get_ptr)();
310 _MonoJitInfo* ji = (*m_mono_jit_info_table_find_ptr)(d,ip);
311 *_start_addr = 0;
312 void* start_addr = 0;
313 if (ji){
314 start_addr = (*m_mono_jit_info_get_code_start_ptr)(ji);
315 _MonoMethod* method = (*m_mono_jit_info_get_method_ptr)(ji);
316 func_name = (*m_mono_method_full_name_ptr)(method,true);
317 }
318 //cout << "** START ADDR=" << start_addr << " func_name=" << (void*)func_name << " IP=" << ip << '\n';
319 //if (func_name)
320 //cout << "** FUNC=" << func_name << '\n';
321 *_start_addr = start_addr;
322 return func_name;
323 }
324
325 private:
326
327 bool m_is_valid = false;
328 mono_jit_info_get_code_start_func m_mono_jit_info_get_code_start_ptr = nullptr;
329 mono_domain_get_func m_mono_domain_get_ptr = nullptr;
330 mono_jit_info_table_find_func m_mono_jit_info_table_find_ptr = nullptr;
331 mono_pmip_func m_mono_pmip_ptr = nullptr;
332 mono_jit_info_get_method_func m_mono_jit_info_get_method_ptr = nullptr;
333 mono_method_full_name_func m_mono_method_full_name_ptr = nullptr;
334 void* m_handle = nullptr;
335};
336
337/*---------------------------------------------------------------------------*/
338/*---------------------------------------------------------------------------*/
339
340static std::atomic<Int32> global_in_malloc;
341
342#ifdef ARCANE_USE_MALLOC_HOOK
343
344namespace{
345void*(*my_old_malloc_hook)(size_t,const void*);
346void(*my_old_free_hook)(void*,const void*);
347void*(*my_old_realloc_hook)(void* __ptr,size_t __size, __const void*);
348
349}
350
351//static Arcane::Int64 global_nb_malloc = 1;
352extern void* prof_malloc_hook(size_t size,const void* caller);
353extern void prof_free_hook(void* ptr,const void* caller);
354extern void* prof_realloc_hook(void* __ptr,size_t __size,__const void*);
355
356// Ces fonctions ne doivent pas être statiques pour éviter une optimisation
357// de GCC 4.7.1 qui fait une boucle infinie dans prof_malloc_hook
358// (Note: clang 3.4 a le meme comportement)
359extern ARCANE_STD_EXPORT void _pushHook()
360{
361 __malloc_hook = my_old_malloc_hook;
362 __free_hook = my_old_free_hook;
363 __realloc_hook = my_old_realloc_hook;
364}
365
366extern ARCANE_STD_EXPORT void _popHook()
367{
368 __malloc_hook = prof_malloc_hook;
369 __free_hook = prof_free_hook;
370 __realloc_hook = prof_realloc_hook;
371}
372
373void* prof_malloc_hook(size_t size,const void* /*caller*/)
374{
375 ++global_in_malloc;
376 _pushHook();
377 void* r = malloc(size);
378 _popHook();
379 --global_in_malloc;
380 return r;
381}
382
383void prof_free_hook(void* ptr,const void* /*caller*/)
384{
385 _pushHook();
386 free(ptr);
387 _popHook();
388}
389
390void* prof_realloc_hook(void* ptr,size_t size,const void* /*caller*/)
391{
392 ++global_in_malloc;
393 _pushHook();
394 void* r = realloc(ptr,size);
395 _popHook();
396 --global_in_malloc;
397 return r;
398}
399
400void _profInitMallocHook()
401{
402 global_in_malloc = 0;
403 my_old_malloc_hook = __malloc_hook;
404 __malloc_hook = prof_malloc_hook;
405 my_old_free_hook = __free_hook;
406 __free_hook = prof_free_hook;
407 my_old_realloc_hook = __realloc_hook;
408 __realloc_hook = prof_realloc_hook;
409}
410
411void _profRestoreMallocHook()
412{
413 __free_hook = my_old_free_hook;
414 __malloc_hook = my_old_malloc_hook;
415 __realloc_hook = my_old_realloc_hook;
416}
417#else
418void _profInitMallocHook()
419{
420}
421void _profRestoreMallocHook()
422{
423}
424#endif
425
426
427/*---------------------------------------------------------------------------*/
428/*---------------------------------------------------------------------------*/
429
430namespace
431{
441const char*
442_getDemangledName(const char* true_func_name,char* demangled_buf,size_t demangled_buf_len)
443{
444 if (demangled_buf_len<=1)
445 return true_func_name;
446 size_t len = demangled_buf_len - 1;
447 int dstatus = 0;
448 const char* buf = nullptr;
449#ifdef __GNUG__
450 buf = abi::__cxa_demangle(true_func_name,demangled_buf,&len,&dstatus);
451#endif
452 if (!buf)
453 buf = true_func_name;
454 return buf;
455}
456} // End anonymous namespace
457
458/*---------------------------------------------------------------------------*/
459/*---------------------------------------------------------------------------*/
460
461ProfInfos::
462ProfInfos(ITraceMng* tm)
463: TraceAccessor(tm)
464, m_default_func_info_provider(new NullFuncInfoProvider())
465, m_func_info_provider(m_default_func_info_provider)
466{
467 for( Integer i=0; i<MAX_COUNTER; ++i )
468 m_counters[i] = 0;
470 // Pour l'instant, active uniquement si variable d'environnement
471 // positionnée.
472 if (!platform::getEnvironmentVariable("ARCANE_DOTNET_BACKTRACE").null()){
473 m_mono_func_getter = new MonoFuncAddrGetter();
474 if (!m_mono_func_getter->isValid()){
475 delete m_mono_func_getter;
476 m_mono_func_getter = 0;
477 }
478 }
479 }
480#ifdef ARCANE_HAS_PACKAGE_LIBUNWIND
481 m_libunwind_func_info_provider = new LibUnwindFuncInfos();
482 m_use_libunwind = true;
483#endif
484#ifdef ARCANE_HAS_GLIBC_BACKTRACE
485 m_backtrace_func_info_provider = new BacktraceFuncInfos();
486#endif
487}
488
489/*---------------------------------------------------------------------------*/
490/*---------------------------------------------------------------------------*/
491
492ProfInfos::
493~ProfInfos()
494{
495 delete m_default_func_info_provider;
496 delete m_backtrace_func_info_provider;
497 delete m_libunwind_func_info_provider;
498 delete m_mono_func_getter;
499}
500
501/*---------------------------------------------------------------------------*/
502/*---------------------------------------------------------------------------*/
503
504ProfFuncInfo* ProfInfos::
505_getNextFuncInfo()
506{
507 // TODO: utiliser un atomic pour m_current_func_info lorsqu'il faudra le
508 // rendre thread-safe.
509 ProfFuncInfo* fi = &m_func_info_buffer[m_current_func_info];
510 fi->setIndex(m_current_func_info);
511 ++m_current_func_info;
512 return fi;
513}
514
515/*---------------------------------------------------------------------------*/
516/*---------------------------------------------------------------------------*/
517
518void ProfInfos::
519startProfiling()
520{
521 _checkNotStarted();
522 m_is_started = true;
523 info() << "START PROFILING";
524#ifndef __clang__
525 //GG Si on utilise ces hook avec clang il part en boucle infini.
526 // Pour éviter cela, on le désactive et cela semble fonctionner.
527 // De toute facon ces hook sont obsolètes et il faudra penser à faire
528 // autrement.
529 _profInitMallocHook();
530#endif
531
532 String stack_unwinder_str = platform::getEnvironmentVariable("ARCANE_PROFILING_STACKUNWINDING");
533 info() << "STACK_UNWIND=" << stack_unwinder_str;
534 if (stack_unwinder_str=="backtrace"){
535 m_use_backtrace = true;
536 m_use_libunwind = false;
537 }
538 m_func_info_provider = m_default_func_info_provider;
539 if (m_use_backtrace)
540 m_func_info_provider = m_backtrace_func_info_provider;
541 else if (m_use_libunwind)
542 m_func_info_provider = m_libunwind_func_info_provider;
543 info() << "Start profiling: use_backtrace=" << m_use_backtrace
544 << " use_libunwind=" << m_use_libunwind
545 << " has_mono=" << m_mono_func_getter;
546}
547
548/*---------------------------------------------------------------------------*/
549/*---------------------------------------------------------------------------*/
550
551void ProfInfos::
552stopProfiling()
553{
554 m_is_started = false;
555 _profRestoreMallocHook();
556
557 // TODO: comme on incrémente, ne faire que à partir de la dernière méthode
558 // dont le nom est inconnu.
559 ARCANE_CHECK_POINTER(m_func_info_provider);
560 for( Int32 i=0, n=m_current_func_info; i<n; ++i ){
561 ProfFuncInfo& pfi = m_func_info_buffer[i];
562 if (!pfi.hasFuncName())
563 m_func_info_provider->fillFuncName(pfi);
564 }
565}
566
567/*---------------------------------------------------------------------------*/
568/*---------------------------------------------------------------------------*/
569
570void ProfInfos::
571_checkNotStarted()
572{
573 if (m_is_started)
574 ARCANE_FATAL("Invalid call when profiling is active");
575}
576
577/*---------------------------------------------------------------------------*/
578/*---------------------------------------------------------------------------*/
579
580void ProfInfos::
581setFunctionDepth(int v)
582{
583 _checkNotStarted();
584 m_function_depth = v;
585}
586
587/*---------------------------------------------------------------------------*/
588/*---------------------------------------------------------------------------*/
589
590void ProfInfos::
591setPeriod(int v)
592{
593 _checkNotStarted();
594 m_period = v;
595}
596
597/*---------------------------------------------------------------------------*/
598/*---------------------------------------------------------------------------*/
599namespace
600{
601int global_nb_in_handler = 0;
602}
603
604void ProfInfos::
605addEvent(void* address,int overflow_event[MAX_COUNTER],int nb_overflow_event)
606{
607 static bool is_in_handler = false;
608
609 if (is_in_handler){
610 ++global_nb_in_handler;
611 return;
612 }
613 is_in_handler = true;
614 if (m_use_backtrace){
615 //_addEventBacktrace(address,overflow_event,nb_overflow_event);
616#ifdef ARCANE_HAS_GLIBC_BACKTRACE
617 BacktraceStackInfo stack_info((BacktraceFuncInfos*)m_backtrace_func_info_provider);
618 _addEvent(address,overflow_event,nb_overflow_event,stack_info,m_function_depth+2);
619#endif
620 }
621 else if (m_use_libunwind){
622#ifdef ARCANE_HAS_PACKAGE_LIBUNWIND
623 LibUnwindStackInfo stack_info((LibUnwindFuncInfos*)m_libunwind_func_info_provider);
624 _addEvent(address,overflow_event,nb_overflow_event,stack_info,m_function_depth+1);
625#endif
626 }
627 is_in_handler = false;
628}
629
630/*---------------------------------------------------------------------------*/
631/*---------------------------------------------------------------------------*/
632
633void ProfInfos::
634_sortFunctions(std::set<ProfFuncInfo*,ProfFuncComparer>& sorted_func)
635{
636 for( const auto& x : m_func_map )
637 if (x.second)
638 sorted_func.insert(x.second);
639}
640
641/*---------------------------------------------------------------------------*/
642/*---------------------------------------------------------------------------*/
643
644void ProfInfos::
645_storeAddress(void* address,bool is_counter0,int overflow_event[MAX_COUNTER],int nb_overflow_event,
646 bool* do_add,bool* do_stack,bool* func_already_added)
647{
648 ++m_total_event;
649 ProfInfos::AddrMap::iterator v = m_addr_map.find(address);
650 if (v!=m_addr_map.end()){
651 ProfAddrInfo& ai = v->second;
652 for( int i=0; i<nb_overflow_event; ++i )
653 ++ai.m_counters[ overflow_event[i] ];
654 if (ai.m_func_info){
655 for( int i=0; i<nb_overflow_event; ++i )
656 ++ai.m_func_info->m_counters[ overflow_event[i] ];
657 *func_already_added = true;
658 // Si on a déjà suffisamment d'évènements et que notre méthode
659 // dépasse les 1% du temps passé, conserve la pile associée.
660 if (m_total_event>m_nb_event_before_getting_stack){
661 if ((ai.m_func_info->m_counters[0]*100)>m_total_event){
662 ai.m_func_info->m_do_stack = true;
663 if (is_counter0)
664 *do_stack = true;
665 }
666 }
667 }
668 }
669 else
670 *do_add = true;
671}
672
673/*---------------------------------------------------------------------------*/
674/*---------------------------------------------------------------------------*/
675
676void ProfInfos::
677_addEvent(void* address,int overflow_event[MAX_COUNTER],int nb_overflow_event,
678 IStackInfoProvider& stack_info,Integer function_depth)
679{
680 // Si on est dans un malloc, ne fait rien.
681 // TODO: il faudrait quand meme incrementer le compteur correspondant
682 // car dans ce cas le temps passé dans les malloc/realloc/free n'est pas pris en compte
683 // TODO: faire test atomic
684 if (global_in_malloc!=0){
685 //cout << "V=" <<global_in_malloc << '\n';
686 return;
687 }
688
689 bool is_counter0 = false;
690
691 for( int i=0; i<nb_overflow_event; ++i ){
692 if (overflow_event[i]==0)
693 is_counter0 = true;
694 if (overflow_event[i]<0 || overflow_event[i]>=MAX_COUNTER)
695 cerr << "arcane_papi_handler: EVENT ERROR n=" << overflow_event[i] << '\n';
696 }
697
698 bool do_stack = false;
699 bool do_add = false;
700 bool func_already_added = false;
701 _storeAddress(address,is_counter0,overflow_event,nb_overflow_event,
702 &do_add,&do_stack,&func_already_added);
703
704 if (do_add || do_stack){
705 ProfAddrInfo papi_address_info;
706 ProfStackInfo papi_stack_info;
707 for( int i=0; i<nb_overflow_event; ++i )
708 ++papi_address_info.m_counters[ overflow_event[i] ];
709
710 stack_info.fillStack(function_depth);
711
712 for (Integer index=function_depth, nb_index=stack_info.nbIndex(); index<nb_index; ++index ){
713 intptr_t proc_start = stack_info.functionStartAddress(index);
714
715 ProfInfos::FuncMap::iterator func = m_func_map.find(proc_start);
716 ProfFuncInfo* papi_func_info = nullptr;
717 if (func==m_func_map.end()){
718 if (m_current_func_info>=MAX_FUNC){
719 cerr << "arcane_papi_handler: MAX_FUNC reached !\n";
720 break;
721 }
722 papi_func_info = _getNextFuncInfo();
723 papi_address_info.m_func_info = papi_func_info;
724 Int32 func_index = papi_func_info->index();
725 stack_info.setFunc(func_index,index);
726 m_func_map.insert(ProfInfos::FuncMap::value_type(proc_start,papi_func_info));
727 }
728 else{
729 papi_func_info = func->second;
730 }
731
732 if (index<(MAX_STACK+function_depth))
733 papi_stack_info.m_funcs_info_indexes[index-function_depth] = papi_func_info->index();
734 if (index==function_depth){
735 papi_address_info.m_func_info = papi_func_info;
736 if (!func_already_added)
737 for( int i=0; i<nb_overflow_event; ++i )
738 ++papi_func_info->m_counters[ overflow_event[i] ];
739 if (!papi_func_info->m_do_stack || !is_counter0)
740 break;
741 else
742 do_stack = true;
743 }
744 }
745 if (do_stack){
746 ++m_total_stack;
747 ProfInfos::StackMap::iterator st = m_stack_map.find(papi_stack_info);
748 if (st!=m_stack_map.end()){
749 ++st->second;
750 }
751 else
752 m_stack_map.insert(ProfInfos::StackMap::value_type(papi_stack_info,1));
753 }
754 if (do_add)
755 m_addr_map.insert(ProfInfos::AddrMap::value_type(address,papi_address_info));
756 }
757}
758
759/*---------------------------------------------------------------------------*/
760/*---------------------------------------------------------------------------*/
761// NOTE: cette méthode n'est plus utilisée mais le mécanisme d'accès via mono
762// doit être intégré au code de libunwind ou backtrace.
763bool ProfInfos::
764_getFunc(void* addr,FuncAddrInfo& info)
765{
766#ifdef ARCANE_OS_LINUX
767 Dl_info dl_info;
768 int r = dladdr(addr,&dl_info);
769 info.func_name = "unknown";
770 info.start_addr = 0;
771 if (r!=0){
772 // Il est possible que dladdr ne retourne pas d'erreur mais ne trouve
773 // pas le symbole. Dans ce cas, essaie de voir s'il s'agit d'un
774 // symbole C#.
775 info.start_addr = dl_info.dli_saddr;
776 if (dl_info.dli_sname){
777 info.func_name = dl_info.dli_sname;
778 return false;
779 }
780 }
781 if (m_mono_func_getter){
782 void* start_addr = 0;
783 char* func_name = m_mono_func_getter->getInfo(addr,&start_addr);
784 if (func_name && start_addr){
785 info.start_addr = start_addr;
786 info.func_name = func_name;
787 return false;
788 }
789 }
790#endif
791 return true;
792}
793
794/*---------------------------------------------------------------------------*/
795/*---------------------------------------------------------------------------*/
796
797void ProfInfos::
798reset()
799{
800 ProfInfos* global_infos = this;
801 {
802 ProfInfos::AddrMap::iterator begin = global_infos->m_addr_map.begin();
803 for( ; begin!=global_infos->m_addr_map.end(); ++begin ){
804 for( Integer i=0; i<MAX_COUNTER; ++i )
805 begin->second.m_counters[i] = 0;
806 }
807 }
808 {
809 ProfInfos::FuncMap::iterator begin = global_infos->m_func_map.begin();
810 for( ; begin!=global_infos->m_func_map.end(); ++begin ){
811 ProfFuncInfo* pf = begin->second;
812 for( Integer i=0; i<MAX_COUNTER; ++i )
813 pf->m_counters[i] =0;
814 }
815 }
816 m_total_event = 0;
817}
818
819/*---------------------------------------------------------------------------*/
820/*---------------------------------------------------------------------------*/
821
822void ProfInfos::
823printInfos(bool dump_file)
824{
825 ProfInfos* global_infos = this;
826
827 Int64 total_event = 1;
828 Int64 total_fp = 1;
829 int process_id = platform::getProcessId();
830 {
831 info() << " PROCESS_ID = " << process_id;
832 info() << " NB ADDRESS MAP = " << global_infos->m_addr_map.size();
833 info() << " NB FUNC MAP = " << global_infos->m_func_map.size();
834 info() << " NB STACK MAP = " << global_infos->m_stack_map.size();
835 info() << " TOTAL STACK = " << global_infos->m_total_stack;
836 {
837 ProfInfos::AddrMap::const_iterator begin = global_infos->m_addr_map.begin();
838 for( ; begin!=global_infos->m_addr_map.end(); ++begin ){
839 Int64 nb_event = begin->second.m_counters[0];
840 total_event += nb_event;
841 Int64 nb_fp = begin->second.m_counters[2];
842 total_fp += nb_fp;
843 }
844 }
845 {
846 ProfInfos::FuncMap::const_iterator begin = global_infos->m_func_map.begin();
847 Int64 total_func_event = 0;
848 Int64 total_func_fp = 0;
849 for( ; begin!=global_infos->m_func_map.end(); ++begin ){
850 ProfFuncInfo* pf = begin->second;
851 Int64 nb_event = pf->m_counters[0];
852 total_func_event += nb_event;
853 Int64 nb_fp = pf->m_counters[2];
854 total_func_fp += nb_fp;
855 }
856 info() << " FUNC EVENT=" << total_func_event;
857 info() << " FUNC FP=" << total_func_fp;
858 }
859
860 if (dump_file){
861 StringBuilder sfile_name = "profiling.addr";
862 sfile_name += process_id;
863 sfile_name += ".xml";
864 String file_name = sfile_name;
865 std::ofstream ofile(file_name.localstr());
866 ofile << "<?xml version='1.0'?>\n";
867 ofile << "<addresses>\n";
868 ProfInfos::AddrMap::const_iterator begin = global_infos->m_addr_map.begin();
869 for( ; begin!=global_infos->m_addr_map.end(); ++begin ){
870 void* addr = begin->first;
871 const ProfAddrInfo& pa = begin->second;
872 ProfFuncInfo* fi = pa.m_func_info;
873 if (fi){
874 ofile << "<addr addr='" << addr << "'"
875 << " count='" << pa.m_counters[0] << "'"
876 << " fi='" << fi << "'"
877 << " fi_count='" << fi->m_counters[0] << "'"
878 << " func='" << fi->m_func_name << "'"
879 << ">\n";
880 }
881 }
882 ofile << "/<addresses>\n";
883 }
884 }
885
886 std::set<ProfFuncInfo*,ProfFuncComparer> sorted_func;
887 _sortFunctions(sorted_func);
888
889 const size_t NAME_BUF_SIZE = 8120;
890 char demangled_func_name[NAME_BUF_SIZE];
891 {
892 info() << " TOTAL EVENT = " << total_event;
893 info() << " TOTAL NB_IN_HANDLER = " << global_nb_in_handler;
894 Real nb_gflop = ((Real)total_fp*(Real)m_period) * 1e-09;
895 info() << " TOTAL FP = " << total_fp << " (nb_giga_flip=" << nb_gflop << ")";
896 info() << " RATIO FP/CYC = " << ((Real)total_fp/(Real)total_event);
897 info() << " " << Trace::Width(10) << "nb_event"
898 << " " << Trace::Width(5) << "%"
899 << " " << Trace::Width(5) << "cum%"
900 << " " << Trace::Width(12) << "event1"
901 << " " << Trace::Width(12) << "event2"
902 << " " << Trace::Width(12) << "event3"
903 << " " << " "
904 << " " << "function";
905 Integer index = 0;
906 Int64 cumulative_nb_event = 0;
907 for( ProfFuncInfo* pfi : sorted_func ){
908 const char* func_name = pfi->m_func_name;
909 const char* buf = _getDemangledName(func_name,demangled_func_name,NAME_BUF_SIZE);
910 Int64 nb_event = pfi->m_counters[0];
911 cumulative_nb_event += nb_event;
912 Int64 cumulative_total_percent = (cumulative_nb_event * 1000) / total_event;
913 Int64 total_percent = (nb_event * 1000) / total_event;
914 Int64 cumulative_percent = (cumulative_total_percent/10);
915 Int64 percent = (total_percent/10);
916 info() << " " << Trace::Width(10) << nb_event
917 << " " << Trace::Width(3) << percent << "." << (total_percent % 10)
918 << " " << Trace::Width(3) << cumulative_percent << "." << (cumulative_total_percent % 10)
919 << " " << Trace::Width(12) << pfi->m_counters[0]
920 << " " << Trace::Width(12) << pfi->m_counters[1]
921 << " " << Trace::Width(12) << pfi->m_counters[2]
922 << " " << pfi->m_do_stack
923 << " " << buf;
924 if (total_percent<5 && index>20)
925 break;
926 ++index;
927 }
928 }
929 // TODO: Calculer ces informations lors de l'arrêt du profiling.
930 if (dump_file){
931 // Créée une liste des piles triée par nombre d'évènements décroissant.
932 UniqueArray<SortedProfStackInfo> sorted_stacks;
933 for( const auto& x : global_infos->m_stack_map ){
934 const ProfStackInfo& psi = x.first;
935 Int64 nb_stack = x.second;
936 sorted_stacks.add(SortedProfStackInfo(psi,nb_stack));
937 }
938 std::sort(std::begin(sorted_stacks),std::end(sorted_stacks));
939
940 String file_name = String("profiling.callstack") + platform::getProcessId();
941 std::ofstream ofile;
942 ofile.open(file_name.localstr());
943
944 for( const auto& x : sorted_stacks ){
945 const ProfStackInfo& psi = x.stackInfo();
946 Int64 nb_stack = x.nbCount();
947 if (nb_stack<2)
948 continue;
949 ofile << " Stack nb=" << nb_stack << '\n';
950 for( Integer z=0; z<MAX_STACK; ++z ){
951 Int32 fidx = psi.m_funcs_info_indexes[z];
952 if (fidx>=0){
953 ProfFuncInfo& pfi = _funcInfoFromIndex(fidx);
954 const char* func_name = pfi.m_func_name;
955 const char* buf = _getDemangledName(func_name,demangled_func_name,NAME_BUF_SIZE);
956 ofile << " " << buf << '\n';
957 }
958 }
959 }
960 ofile.close();
961 }
962}
963
964
965// ****************************************************************************
966// * getInfos for someone else
967// * [0] 0xb80dd1a3ul
968// * [1] Size
969// * [2] total_event
970// * [3] total_fp
971// * [4] (Int64) int m_period
972// * [5] (Int64) Integer index;
973// * nb_event
974// * total_percent
975// * m_counters[0]: PAPI_TOT_CYC Total cycles
976// * m_counters[1]: PAPI_RES_STL Cycles stalled on any resource
977// * m_counters[2]: PAPI_FP_INS Floating point instructions
978// * strlen(func_name)
979// *
980// *
981// ****************************************************************************
982void ProfInfos::
983getInfos(Int64Array& pkt)
984{
985 const size_t NAME_BUF_SIZE = 8192;
986 char demangled_func_name[NAME_BUF_SIZE];
987 ProfInfos* global_infos = this;
988 Int64 total_event = 1;
989 Int64 total_fp = 1;
990 Integer index = 0;
991
992 {
993 ProfInfos::AddrMap::const_iterator begin = global_infos->m_addr_map.begin();
994 for( ; begin!=global_infos->m_addr_map.end(); ++begin ){
995 Int64 nb_event = begin->second.m_counters[0];
996 total_event += nb_event;
997 Int64 nb_fp = begin->second.m_counters[2];
998 total_fp += nb_fp;
999 }
1000 }
1001 //Real nb_gflop = ((Real)total_fp*(Real)m_period) * 1e-09;
1002 pkt.add(total_event);
1003 pkt.add(total_fp);
1004 pkt.add((Int64)m_period);
1005 info()<<"[ProfInfos::getInfos] total_event="<<total_event<<", total_fp="<<total_fp<<", m_period="<<m_period;
1006
1007 std::set<ProfFuncInfo*,ProfFuncComparer> sorted_func;
1008 _sortFunctions(sorted_func);
1009
1010 // On pousse l'index qui vaut zero
1011 pkt.add((Int64)index);
1012
1013 // Et on continue à remplir l'array
1014 for( ProfFuncInfo* pfi : sorted_func ){
1015 const char* func_name = pfi->m_func_name;
1016 const char* buf = _getDemangledName(func_name,demangled_func_name,NAME_BUF_SIZE);
1017 Int64 nb_event = pfi->m_counters[0];
1018 pkt.add(nb_event);
1019 Int64 total_percent = (nb_event * 1000) / total_event;
1020 pkt.add(total_percent);
1021 pkt.add(pfi->m_counters[0]);
1022 pkt.add(pfi->m_counters[1]);
1023 pkt.add(pfi->m_counters[2]);
1024
1025 Int64 mx = strlen(buf);
1026 pkt.add(mx);
1027 for(Int64 i=0; i<mx; i+=8)
1028 pkt.add(*(Int64*)&buf[i]);
1029
1030 if (total_percent<1 && index>16)
1031 break;
1032 ++index;
1033 }
1034 pkt[5]=(Int64)(index);
1035 info()<<"[ProfInfos::getInfos] index @[4] ="<<pkt[5];
1036}
1037
1038/*---------------------------------------------------------------------------*/
1039/*---------------------------------------------------------------------------*/
1040
1041void ProfInfos::
1042dumpJSON(JSONWriter& writer)
1043{
1044 // TODO: utiliser un identifiant pour les noms de fonction au lieu de
1045 // mettre directement le nom de la méthode.
1046
1047 ProfInfos* global_infos = this;
1048
1049 Int64 total_event = 1;
1050 for( const auto& x : global_infos->m_addr_map ){
1051 Int64 nb_event = x.second.m_counters[0];
1052 total_event += nb_event;
1053 }
1054
1055 {
1056 int process_id = platform::getProcessId();
1057 writer.write("ProcessId",(Int64)process_id);
1058 writer.write("NbAddressMap",global_infos->m_addr_map.size());
1059 writer.write("NbFuncMap",global_infos->m_func_map.size());
1060 writer.write("NbStackMap",global_infos->m_stack_map.size());
1061 writer.write("TotalStack",global_infos->m_total_stack);
1062 writer.write("TotalEvent",total_event);
1063 }
1064
1065 std::set<ProfFuncInfo*,ProfFuncComparer> sorted_func;
1066 _sortFunctions(sorted_func);
1067
1068 const size_t NAME_BUF_SIZE = 8192;
1069 char demangled_func_name[NAME_BUF_SIZE];
1070
1071 // Ecrit la liste des méthodes référencées et leur nom manglé et démanglé
1072 {
1073 writer.writeKey("Functions");
1074 writer.beginArray();
1075 for( Int32 i=0, n=m_current_func_info; i<n; ++i ){
1076 const ProfFuncInfo& pfi = m_func_info_buffer[i];
1077 const char* func_name = pfi.m_func_name;
1078 const char* buf = _getDemangledName(func_name,demangled_func_name,NAME_BUF_SIZE);
1079
1080 {
1081 JSONWriter::Object o(writer);
1082 writer.write("Index",(Int64)pfi.index());
1083 writer.write("Name",func_name);
1084 writer.write("DemangledName",buf);
1085 }
1086
1087 }
1088 writer.endArray();
1089 }
1090
1091 // Ecrit la liste des méthodes triées par ordre décroissant des valeurs
1092 // des compteurs.
1093 {
1094 Integer index = 0;
1095 writer.writeKey("SortedFuncTimes");
1096 writer.beginArray();
1097 for( ProfFuncInfo* pfi : sorted_func ){
1098 const char* func_name = pfi->m_func_name;
1099 const char* buf = _getDemangledName(func_name,demangled_func_name,NAME_BUF_SIZE);
1100 {
1101 JSONWriter::Object o(writer);
1102 writer.write("Index",(Int64)(pfi->index()));
1103 writer.write("Name",buf);
1104 writer.write("Events",Int64ConstArrayView(3,pfi->m_counters));
1105 }
1106 if (index>100)
1107 break;
1108 ++index;
1109 }
1110 writer.endArray();
1111 }
1112
1113 {
1114 // Créée une liste des piles triée par nombre d'évènements décroissant.
1115 UniqueArray<SortedProfStackInfo> sorted_stacks;
1116 for( const auto& x : global_infos->m_stack_map ){
1117 const ProfStackInfo& psi = x.first;
1118 Int64 nb_stack = x.second;
1119 sorted_stacks.add(SortedProfStackInfo(psi,nb_stack));
1120 }
1121 std::sort(std::begin(sorted_stacks),std::end(sorted_stacks));
1122
1123 writer.writeKey("StackMap");
1124 writer.beginArray();
1125 for( const auto& x : sorted_stacks ){
1126 const ProfStackInfo& psi = x.stackInfo();
1127 Int64 nb_stack = x.nbCount();
1128 if (nb_stack<2)
1129 continue;
1130 {
1131 JSONWriter::Object o(writer);
1132 writer.write("Count",nb_stack);
1133 writer.write("Stacks",Int32ConstArrayView(MAX_STACK,psi.m_funcs_info_indexes));
1134
1135 writer.writeKey("StackNames");
1136 writer.beginArray();
1137 for( Integer z=0; z<MAX_STACK; ++z ){
1138 Int32 fidx = psi.m_funcs_info_indexes[z];
1139 if (fidx>=0){
1140 ProfFuncInfo& pfi = _funcInfoFromIndex(fidx);
1141 const char* func_name = pfi.m_func_name;
1142 const char* buf = _getDemangledName(func_name,demangled_func_name,NAME_BUF_SIZE);
1143 writer.writeValue(buf);
1144 }
1145 else
1146 writer.writeValue(String());
1147 }
1148 writer.endArray();
1149 }
1150 }
1151 writer.endArray();
1152 }
1153}
1154
1155/*---------------------------------------------------------------------------*/
1156/*---------------------------------------------------------------------------*/
1157
1158} // End namespace Arcane
1159
1160/*---------------------------------------------------------------------------*/
1161/*---------------------------------------------------------------------------*/
Fichier de configuration d'Arcane.
#define ARCANE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
Interface pour récupérer les infos d'une pile d'appel.
TraceMessage info() const
Flot pour un message d'information.
Formattage du flot en longueur.
Integer len(const char *s)
Retourne la longueur de la chaîne s.
bool hasDotNETRuntime()
Vrai si le code s'exécute avec le runtime .NET.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
Array< Int64 > Int64Array
Tableau dynamique à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:325
ConstArrayView< Int32 > Int32ConstArrayView
Equivalent C d'un tableau à une dimension d'entiers 32 bits.
Definition UtilsTypes.h:640
ConstArrayView< Int64 > Int64ConstArrayView
Equivalent C d'un tableau à une dimension d'entiers 64 bits.
Definition UtilsTypes.h:638
Int32 Integer
Type représentant un entier.