Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
ProfilingInfo.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/* ProfilingInfo.cc (C) 2000-2023 */
9/* */
10/* Profiling information. */
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/*---------------------------------------------------------------------------*/
69
76{
77 public:
78
79 virtual ~IStackInfoProvider() {}
80 virtual Integer nbIndex() const = 0;
81 virtual intptr_t functionStartAddress(Int32 stack_index) = 0;
82 virtual void fillStack(Integer function_depth) = 0;
83 virtual void setFunc(Int32 func_index, Int32 stack_index) = 0;
84};
85
86/*---------------------------------------------------------------------------*/
87/*---------------------------------------------------------------------------*/
88
90{
91 public:
92
93 virtual ~IFuncInfoProvider() {}
94 virtual void fillFuncName(ProfFuncInfo& pfi) = 0;
95};
96
97/*---------------------------------------------------------------------------*/
98/*---------------------------------------------------------------------------*/
99
101: public IFuncInfoProvider
102{
103 public:
104
105 virtual ~NullFuncInfoProvider() {}
106 virtual void fillFuncName(ProfFuncInfo& pfi) override
107 {
108 pfi.m_func_name[0] = '\0';
109 pfi.setHasFuncName(true);
110 }
111};
112
113/*---------------------------------------------------------------------------*/
114/*---------------------------------------------------------------------------*/
115
116#ifdef ARCANE_HAS_PACKAGE_LIBUNWIND
117class ProfInfos::LibUnwindFuncInfos
118: public IFuncInfoProvider
119{
120 public:
121
122 void fillFuncName(ProfFuncInfo& pfi) override
123 {
124 Int32 func_index = pfi.index();
125 unw_cursor_t* cursor = &func_cursor[func_index];
126 unw_word_t offset;
127 unw_get_proc_name(cursor, pfi.m_func_name, MAX_FUNC_LEN, &offset);
128 pfi.setHasFuncName(true);
129 }
130 void setFunc(Int32 func_index, const unw_cursor_t& cursor)
131 {
132 func_cursor[func_index] = cursor;
133 }
134
135 private:
136
137 unw_cursor_t func_cursor[MAX_FUNC];
138};
139
140class ProfInfos::LibUnwindStackInfo
141: public IStackInfoProvider
142{
143 public:
144
145 LibUnwindStackInfo(LibUnwindFuncInfos* func_infos)
146 : m_nb_index(0)
147 , m_func_infos(func_infos)
148 {}
149
150 public:
151
152 void fillStack(Integer function_depth) override
153 {
154 Integer index = 0;
155 unw_context_t uc;
156 unw_cursor_t cursor;
157 unw_getcontext(&uc);
158 unw_init_local(&cursor, &uc);
159 unw_proc_info_t proc_info;
160 while (unw_step(&cursor) > 0 && index < (MAX_STACK + function_depth)) {
161 unw_get_proc_info(&cursor, &proc_info);
162 m_cursors[index] = cursor;
163 m_proc_start[index] = proc_info.start_ip;
164 ++index;
165 }
166 m_nb_index = index;
167 }
168 void setFunc(Int32 func_index, Int32 stack_index) override
169 {
170 m_func_infos->setFunc(func_index, m_cursors[stack_index]);
171 }
172
173 public:
174
175 Integer nbIndex() const override { return m_nb_index; }
176 intptr_t functionStartAddress(Int32 stack_index) override
177 {
178 return (intptr_t)m_proc_start[stack_index];
179 }
180
181 private:
182
183 // TODO: check size
184 unw_cursor_t m_cursors[256];
185 unw_word_t m_proc_start[256];
186 Integer m_nb_index;
187 LibUnwindFuncInfos* m_func_infos;
188};
189#endif
190
191/*---------------------------------------------------------------------------*/
192/*---------------------------------------------------------------------------*/
193
194#ifdef ARCANE_HAS_GLIBC_BACKTRACE
195class ProfInfos::BacktraceFuncInfos
196: public IFuncInfoProvider
197{
198 public:
199
200 void fillFuncName(ProfFuncInfo& pfi) override
201 {
202 Int32 func_index = pfi.index();
203 int copy_index = 0;
204 const char* func_name = func_dl_info[func_index].dli_sname;
205 if (func_name) {
206 for (; copy_index < MAX_FUNC_LEN; ++copy_index) {
207 char c = func_name[copy_index];
208 pfi.m_func_name[copy_index] = c;
209 if (c == '\0')
210 break;
211 }
212 }
213 pfi.m_func_name[copy_index] = '\0';
214 pfi.setHasFuncName(true);
215 }
216 void setFunc(Int32 func_index, const Dl_info& dl_info)
217 {
218 func_dl_info[func_index] = dl_info;
219 }
220
221 private:
222
223 Dl_info func_dl_info[MAX_FUNC];
224};
225class ProfInfos::BacktraceStackInfo
226: public IStackInfoProvider
227{
228 public:
229
230 BacktraceStackInfo(BacktraceFuncInfos* func_infos)
231 : m_nb_index(0)
232 , m_func_infos(func_infos)
233 {}
234
235 public:
236
237 void fillStack(Integer function_depth) override
238 {
239 ARCANE_UNUSED(function_depth);
240 void* addrs[64];
241 m_nb_index = backtrace(addrs, 64);
242 for (Integer index = 0; index < m_nb_index; ++index) {
243 int err_code = dladdr(addrs[index], &m_dl_infos[index]);
244 if (err_code != 0)
245 m_proc_start[index] = (intptr_t)m_dl_infos[index].dli_saddr;
246 else
247 m_proc_start[index] = 0;
248 }
249 }
250 void setFunc(Int32 func_index, Int32 stack_index) override
251 {
252 m_func_infos->setFunc(func_index, m_dl_infos[stack_index]);
253 }
254
255 public:
256
257 Integer nbIndex() const override { return m_nb_index; }
258 intptr_t functionStartAddress(Int32 stack_index) override
259 {
260 return (intptr_t)m_proc_start[stack_index];
261 }
262
263 private:
264
265 // TODO: check size
266 std::array<Dl_info, 256> m_dl_infos = {};
267 std::array<intptr_t, 256> m_proc_start = {};
268 Integer m_nb_index;
269 BacktraceFuncInfos* m_func_infos;
270};
271#endif
272
273/*---------------------------------------------------------------------------*/
274/*---------------------------------------------------------------------------*/
275
276class MonoFuncAddrGetter
277{
278 private:
279
280 struct _MonoJitInfo;
281 struct _MonoDomain;
282 struct _MonoMethod;
283
284 typedef void* (*mono_jit_info_get_code_start_func)(_MonoJitInfo* ji);
285 typedef _MonoDomain* (*mono_domain_get_func)();
286 typedef _MonoJitInfo* (*mono_jit_info_table_find_func)(_MonoDomain*, void* ip);
287 typedef char* (*mono_pmip_func)(void* ip);
288 typedef _MonoMethod* (*mono_jit_info_get_method_func)(_MonoJitInfo* ji);
289 typedef char* (*mono_method_full_name_func)(_MonoMethod* method, bool full);
290
291 public:
292
293 MonoFuncAddrGetter()
294 : m_is_valid(false)
295 , m_handle(0)
296 {
297 empty_func_name[0] = '\0';
298#if defined(ARCANE_OS_LINUX)
299 void* handle = dlopen(0, RTLD_LAZY);
300 if (!handle)
301 return;
302 m_handle = handle;
303 m_mono_jit_info_get_code_start_ptr = (mono_jit_info_get_code_start_func)(dlsym(handle, "mono_jit_info_get_code_start"));
304 m_mono_domain_get_ptr = (mono_domain_get_func)(dlsym(handle, "mono_domain_get"));
305 m_mono_jit_info_table_find_ptr = (mono_jit_info_table_find_func)(dlsym(handle, "mono_jit_info_table_find"));
306 m_mono_pmip_ptr = (mono_pmip_func)(dlsym(handle, "mono_pmip"));
307 m_mono_jit_info_get_method_ptr = (mono_jit_info_get_method_func)(dlsym(handle, "mono_jit_info_get_method"));
308 m_mono_method_full_name_ptr = (mono_method_full_name_func)(dlsym(handle, "mono_method_full_name"));
309
310 if (!m_mono_jit_info_get_code_start_ptr)
311 return;
312 if (!m_mono_domain_get_ptr)
313 return;
314 if (!m_mono_jit_info_table_find_ptr)
315 return;
316 if (!m_mono_pmip_ptr)
317 return;
318 if (!m_mono_jit_info_get_method_ptr)
319 return;
320 if (!m_mono_method_full_name_ptr)
321 return;
322 m_is_valid = true;
323#endif
324 }
325 ~MonoFuncAddrGetter()
326 {
327#if defined(ARCANE_OS_LINUX)
328 dlclose(m_handle);
329#endif
330 }
331
332 bool isValid() const
333 {
334 return m_is_valid;
335 }
336 char empty_func_name[1];
337 char* getInfo(void* ip, void** _start_addr)
338 {
339 if (!m_is_valid)
340 return 0;
341 char* func_name = empty_func_name; //(*m_mono_pmip_ptr)(ip);
342 _MonoDomain* d = (*m_mono_domain_get_ptr)();
343 _MonoJitInfo* ji = (*m_mono_jit_info_table_find_ptr)(d, ip);
344 *_start_addr = 0;
345 void* start_addr = 0;
346 if (ji) {
347 start_addr = (*m_mono_jit_info_get_code_start_ptr)(ji);
348 _MonoMethod* method = (*m_mono_jit_info_get_method_ptr)(ji);
349 func_name = (*m_mono_method_full_name_ptr)(method, true);
350 }
351 //cout << "** START ADDR=" << start_addr << " func_name=" << (void*)func_name << " IP=" << ip << '\n';
352 //if (func_name)
353 //cout << "** FUNC=" << func_name << '\n';
354 *_start_addr = start_addr;
355 return func_name;
356 }
357
358 private:
359
360 bool m_is_valid = false;
361 mono_jit_info_get_code_start_func m_mono_jit_info_get_code_start_ptr = nullptr;
362 mono_domain_get_func m_mono_domain_get_ptr = nullptr;
363 mono_jit_info_table_find_func m_mono_jit_info_table_find_ptr = nullptr;
364 mono_pmip_func m_mono_pmip_ptr = nullptr;
365 mono_jit_info_get_method_func m_mono_jit_info_get_method_ptr = nullptr;
366 mono_method_full_name_func m_mono_method_full_name_ptr = nullptr;
367 void* m_handle = nullptr;
368};
369
370/*---------------------------------------------------------------------------*/
371/*---------------------------------------------------------------------------*/
372
373static std::atomic<Int32> global_in_malloc;
374
375#ifdef ARCANE_USE_MALLOC_HOOK
376
377namespace
378{
379 void* (*my_old_malloc_hook)(size_t, const void*);
380 void (*my_old_free_hook)(void*, const void*);
381 void* (*my_old_realloc_hook)(void* __ptr, size_t __size, __const void*);
382
383} // namespace
384
385//static Arcane::Int64 global_nb_malloc = 1;
386extern void* prof_malloc_hook(size_t size, const void* caller);
387extern void prof_free_hook(void* ptr, const void* caller);
388extern void* prof_realloc_hook(void* __ptr, size_t __size, __const void*);
389
390// These functions must not be static to avoid a GCC 4.7.1 optimization
391// that causes an infinite loop in prof_malloc_hook
392// (Note: clang 3.4 has the same behavior)
393extern ARCANE_STD_EXPORT void _pushHook()
394{
395 __malloc_hook = my_old_malloc_hook;
396 __free_hook = my_old_free_hook;
397 __realloc_hook = my_old_realloc_hook;
398}
399
400extern ARCANE_STD_EXPORT void _popHook()
401{
402 __malloc_hook = prof_malloc_hook;
403 __free_hook = prof_free_hook;
404 __realloc_hook = prof_realloc_hook;
405}
406
407void* prof_malloc_hook(size_t size, const void* /*caller*/)
408{
409 ++global_in_malloc;
410 _pushHook();
411 void* r = malloc(size);
412 _popHook();
413 --global_in_malloc;
414 return r;
415}
416
417void prof_free_hook(void* ptr, const void* /*caller*/)
418{
419 _pushHook();
420 free(ptr);
421 _popHook();
422}
423
424void* prof_realloc_hook(void* ptr, size_t size, const void* /*caller*/)
425{
426 ++global_in_malloc;
427 _pushHook();
428 void* r = realloc(ptr, size);
429 _popHook();
430 --global_in_malloc;
431 return r;
432}
433
434void _profInitMallocHook()
435{
436 global_in_malloc = 0;
437 my_old_malloc_hook = __malloc_hook;
438 __malloc_hook = prof_malloc_hook;
439 my_old_free_hook = __free_hook;
440 __free_hook = prof_free_hook;
441 my_old_realloc_hook = __realloc_hook;
442 __realloc_hook = prof_realloc_hook;
443}
444
445void _profRestoreMallocHook()
446{
447 __free_hook = my_old_free_hook;
448 __malloc_hook = my_old_malloc_hook;
449 __realloc_hook = my_old_realloc_hook;
450}
451#else
452void _profInitMallocHook()
453{
454}
455void _profRestoreMallocHook()
456{
457}
458#endif
459
460/*---------------------------------------------------------------------------*/
461/*---------------------------------------------------------------------------*/
462
463namespace
464{
474 const char*
475 _getDemangledName(const char* true_func_name, char* demangled_buf, size_t demangled_buf_len)
476 {
477 if (demangled_buf_len <= 1)
478 return true_func_name;
479 size_t len = demangled_buf_len - 1;
480 int dstatus = 0;
481 const char* buf = nullptr;
482#ifdef __GNUG__
483 buf = abi::__cxa_demangle(true_func_name, demangled_buf, &len, &dstatus);
484#endif
485 if (!buf)
486 buf = true_func_name;
487 return buf;
488 }
489} // End anonymous namespace
490
491/*---------------------------------------------------------------------------*/
492/*---------------------------------------------------------------------------*/
493
494ProfInfos::
495ProfInfos(ITraceMng* tm)
496: TraceAccessor(tm)
497, m_default_func_info_provider(new NullFuncInfoProvider())
498, m_func_info_provider(m_default_func_info_provider)
499{
500 for (Integer i = 0; i < MAX_COUNTER; ++i)
501 m_counters[i] = 0;
503 // For now, only active if environment variable
504 // is set.
505 if (!platform::getEnvironmentVariable("ARCANE_DOTNET_BACKTRACE").null()) {
506 m_mono_func_getter = new MonoFuncAddrGetter();
507 if (!m_mono_func_getter->isValid()) {
508 delete m_mono_func_getter;
509 m_mono_func_getter = 0;
510 }
511 }
512 }
513#ifdef ARCANE_HAS_PACKAGE_LIBUNWIND
514 m_libunwind_func_info_provider = new LibUnwindFuncInfos();
515 m_use_libunwind = true;
516#endif
517#ifdef ARCANE_HAS_GLIBC_BACKTRACE
518 m_backtrace_func_info_provider = new BacktraceFuncInfos();
519#endif
520}
521
522/*---------------------------------------------------------------------------*/
523/*---------------------------------------------------------------------------*/
524
525ProfInfos::
526~ProfInfos()
527{
528 delete m_default_func_info_provider;
529 delete m_backtrace_func_info_provider;
530 delete m_libunwind_func_info_provider;
531 delete m_mono_func_getter;
532}
533
534/*---------------------------------------------------------------------------*/
535/*---------------------------------------------------------------------------*/
536
537ProfFuncInfo* ProfInfos::
538_getNextFuncInfo()
539{
540 // TODO: use an atomic for m_current_func_info when it needs to be
541 // made thread-safe.
542 ProfFuncInfo* fi = &m_func_info_buffer[m_current_func_info];
543 fi->setIndex(m_current_func_info);
544 ++m_current_func_info;
545 return fi;
546}
547
548/*---------------------------------------------------------------------------*/
549/*---------------------------------------------------------------------------*/
550
551void ProfInfos::
552startProfiling()
553{
554 _checkNotStarted();
555 m_is_started = true;
556 info() << "START PROFILING";
557#ifndef __clang__
558 // GG If we use these hooks with clang it goes into an infinite loop.
559 // To avoid this, we disable it and it seems to work.
560 // Anyway, these hooks are obsolete and we will need to think about
561 // doing something else.
562 _profInitMallocHook();
563#endif
564
565 String stack_unwinder_str = platform::getEnvironmentVariable("ARCANE_PROFILING_STACKUNWINDING");
566 info() << "STACK_UNWIND=" << stack_unwinder_str;
567 if (stack_unwinder_str == "backtrace") {
568 m_use_backtrace = true;
569 m_use_libunwind = false;
570 }
571 m_func_info_provider = m_default_func_info_provider;
572 if (m_use_backtrace)
573 m_func_info_provider = m_backtrace_func_info_provider;
574 else if (m_use_libunwind)
575 m_func_info_provider = m_libunwind_func_info_provider;
576 info() << "Start profiling: use_backtrace=" << m_use_backtrace
577 << " use_libunwind=" << m_use_libunwind
578 << " has_mono=" << m_mono_func_getter;
579}
580
581/*---------------------------------------------------------------------------*/
582/*---------------------------------------------------------------------------*/
583
584void ProfInfos::
585stopProfiling()
586{
587 m_is_started = false;
588 _profRestoreMallocHook();
589
590 // TODO: since we are incrementing, only do it starting from the last method
591 // whose name is unknown.
592 ARCANE_CHECK_POINTER(m_func_info_provider);
593 for (Int32 i = 0, n = m_current_func_info; i < n; ++i) {
594 ProfFuncInfo& pfi = m_func_info_buffer[i];
595 if (!pfi.hasFuncName())
596 m_func_info_provider->fillFuncName(pfi);
597 }
598}
599
600/*---------------------------------------------------------------------------*/
601/*---------------------------------------------------------------------------*/
602
603void ProfInfos::
604_checkNotStarted()
605{
606 if (m_is_started)
607 ARCANE_FATAL("Invalid call when profiling is active");
608}
609
610/*---------------------------------------------------------------------------*/
611/*---------------------------------------------------------------------------*/
612
613void ProfInfos::
614setFunctionDepth(int v)
615{
616 _checkNotStarted();
617 m_function_depth = v;
618}
619
620/*---------------------------------------------------------------------------*/
621/*---------------------------------------------------------------------------*/
622
623void ProfInfos::
624setPeriod(int v)
625{
626 _checkNotStarted();
627 m_period = v;
628}
629
630/*---------------------------------------------------------------------------*/
631/*---------------------------------------------------------------------------*/
632namespace
633{
634 int global_nb_in_handler = 0;
635}
636
637void ProfInfos::
638addEvent(void* address, int overflow_event[MAX_COUNTER], int nb_overflow_event)
639{
640 static bool is_in_handler = false;
641
642 if (is_in_handler) {
643 ++global_nb_in_handler;
644 return;
645 }
646 is_in_handler = true;
647 if (m_use_backtrace) {
648 //_addEventBacktrace(address,overflow_event,nb_overflow_event);
649#ifdef ARCANE_HAS_GLIBC_BACKTRACE
650 BacktraceStackInfo stack_info((BacktraceFuncInfos*)m_backtrace_func_info_provider);
651 _addEvent(address, overflow_event, nb_overflow_event, stack_info, m_function_depth + 2);
652#endif
653 }
654 else if (m_use_libunwind) {
655#ifdef ARCANE_HAS_PACKAGE_LIBUNWIND
656 LibUnwindStackInfo stack_info((LibUnwindFuncInfos*)m_libunwind_func_info_provider);
657 _addEvent(address, overflow_event, nb_overflow_event, stack_info, m_function_depth + 1);
658#endif
659 }
660 is_in_handler = false;
661}
662
663/*---------------------------------------------------------------------------*/
664/*---------------------------------------------------------------------------*/
665
666void ProfInfos::
667_sortFunctions(std::set<ProfFuncInfo*, ProfFuncComparer>& sorted_func)
668{
669 for (const auto& x : m_func_map)
670 if (x.second)
671 sorted_func.insert(x.second);
672}
673
674/*---------------------------------------------------------------------------*/
675/*---------------------------------------------------------------------------*/
676
677void ProfInfos::
678_storeAddress(void* address, bool is_counter0, int overflow_event[MAX_COUNTER], int nb_overflow_event,
679 bool* do_add, bool* do_stack, bool* func_already_added)
680{
681 ++m_total_event;
682 ProfInfos::AddrMap::iterator v = m_addr_map.find(address);
683 if (v != m_addr_map.end()) {
684 ProfAddrInfo& ai = v->second;
685 for (int i = 0; i < nb_overflow_event; ++i)
686 ++ai.m_counters[overflow_event[i]];
687 if (ai.m_func_info) {
688 for (int i = 0; i < nb_overflow_event; ++i)
689 ++ai.m_func_info->m_counters[overflow_event[i]];
690 *func_already_added = true;
691 // If we already have enough events and our method
692 // exceeds 1% of the time spent, keep the associated stack.
693 if (m_total_event > m_nb_event_before_getting_stack) {
694 if ((ai.m_func_info->m_counters[0] * 100) > m_total_event) {
695 ai.m_func_info->m_do_stack = true;
696 if (is_counter0)
697 *do_stack = true;
698 }
699 }
700 }
701 }
702 else
703 *do_add = true;
704}
705
706/*---------------------------------------------------------------------------*/
707/*---------------------------------------------------------------------------*/
708
709void ProfInfos::
710_addEvent(void* address, int overflow_event[MAX_COUNTER], int nb_overflow_event,
711 IStackInfoProvider& stack_info, Integer function_depth)
712{
713 // If we are in a malloc, do nothing.
714 // TODO: we should still increment the corresponding counter
715 // because in this case the time spent in malloc/realloc/free is not taken into account
716 // TODO: perform atomic test
717 if (global_in_malloc != 0) {
718 //cout << "V=" <<global_in_malloc << '\n';
719 return;
720 }
721
722 bool is_counter0 = false;
723
724 for (int i = 0; i < nb_overflow_event; ++i) {
725 if (overflow_event[i] == 0)
726 is_counter0 = true;
727 if (overflow_event[i] < 0 || overflow_event[i] >= MAX_COUNTER)
728 cerr << "arcane_papi_handler: EVENT ERROR n=" << overflow_event[i] << '\n';
729 }
730
731 bool do_stack = false;
732 bool do_add = false;
733 bool func_already_added = false;
734 _storeAddress(address, is_counter0, overflow_event, nb_overflow_event,
735 &do_add, &do_stack, &func_already_added);
736
737 if (do_add || do_stack) {
738 ProfAddrInfo papi_address_info;
739 ProfStackInfo papi_stack_info;
740 for (int i = 0; i < nb_overflow_event; ++i)
741 ++papi_address_info.m_counters[overflow_event[i]];
742
743 stack_info.fillStack(function_depth);
744
745 for (Integer index = function_depth, nb_index = stack_info.nbIndex(); index < nb_index; ++index) {
746 intptr_t proc_start = stack_info.functionStartAddress(index);
747
748 ProfInfos::FuncMap::iterator func = m_func_map.find(proc_start);
749 ProfFuncInfo* papi_func_info = nullptr;
750 if (func == m_func_map.end()) {
751 if (m_current_func_info >= MAX_FUNC) {
752 cerr << "arcane_papi_handler: MAX_FUNC reached !\n";
753 break;
754 }
755 papi_func_info = _getNextFuncInfo();
756 papi_address_info.m_func_info = papi_func_info;
757 Int32 func_index = papi_func_info->index();
758 stack_info.setFunc(func_index, index);
759 m_func_map.insert(ProfInfos::FuncMap::value_type(proc_start, papi_func_info));
760 }
761 else {
762 papi_func_info = func->second;
763 }
764
765 if (index < (MAX_STACK + function_depth))
766 papi_stack_info.m_funcs_info_indexes[index - function_depth] = papi_func_info->index();
767 if (index == function_depth) {
768 papi_address_info.m_func_info = papi_func_info;
769 if (!func_already_added)
770 for (int i = 0; i < nb_overflow_event; ++i)
771 ++papi_func_info->m_counters[overflow_event[i]];
772 if (!papi_func_info->m_do_stack || !is_counter0)
773 break;
774 else
775 do_stack = true;
776 }
777 }
778 if (do_stack) {
779 ++m_total_stack;
780 ProfInfos::StackMap::iterator st = m_stack_map.find(papi_stack_info);
781 if (st != m_stack_map.end()) {
782 ++st->second;
783 }
784 else
785 m_stack_map.insert(ProfInfos::StackMap::value_type(papi_stack_info, 1));
786 }
787 if (do_add)
788 m_addr_map.insert(ProfInfos::AddrMap::value_type(address, papi_address_info));
789 }
790}
791
792/*---------------------------------------------------------------------------*/
793/*---------------------------------------------------------------------------*/
794
795// NOTE: this method is no longer used but the access mechanism via mono
796// must be integrated into the libunwind or backtrace code.
797bool ProfInfos::
798_getFunc(void* addr, FuncAddrInfo& info)
799{
800#ifdef ARCANE_OS_LINUX
801 Dl_info dl_info;
802 int r = dladdr(addr, &dl_info);
803 info.func_name = "unknown";
804 info.start_addr = 0;
805 if (r != 0) {
806 // It is possible that dladdr does not return an error but does not find
807 // the symbol. In this case, try to see if it is a
808 // C# symbol.
809 info.start_addr = dl_info.dli_saddr;
810 if (dl_info.dli_sname) {
811 info.func_name = dl_info.dli_sname;
812 return false;
813 }
814 }
815 if (m_mono_func_getter) {
816 void* start_addr = 0;
817 char* func_name = m_mono_func_getter->getInfo(addr, &start_addr);
818 if (func_name && start_addr) {
819 info.start_addr = start_addr;
820 info.func_name = func_name;
821 return false;
822 }
823 }
824#endif
825 return true;
826}
827
828/*---------------------------------------------------------------------------*/
829/*---------------------------------------------------------------------------*/
830
831void ProfInfos::
832reset()
833{
834 ProfInfos* global_infos = this;
835 {
836 ProfInfos::AddrMap::iterator begin = global_infos->m_addr_map.begin();
837 for (; begin != global_infos->m_addr_map.end(); ++begin) {
838 for (Integer i = 0; i < MAX_COUNTER; ++i)
839 begin->second.m_counters[i] = 0;
840 }
841 }
842 {
843 ProfInfos::FuncMap::iterator begin = global_infos->m_func_map.begin();
844 for (; begin != global_infos->m_func_map.end(); ++begin) {
845 ProfFuncInfo* pf = begin->second;
846 for (Integer i = 0; i < MAX_COUNTER; ++i)
847 pf->m_counters[i] = 0;
848 }
849 }
850 m_total_event = 0;
851}
852
853/*---------------------------------------------------------------------------*/
854/*---------------------------------------------------------------------------*/
855
856void ProfInfos::
857printInfos(bool dump_file)
858{
859 ProfInfos* global_infos = this;
860
861 Int64 total_event = 1;
862 Int64 total_fp = 1;
863 int process_id = platform::getProcessId();
864 {
865 info() << " PROCESS_ID = " << process_id;
866 info() << " NB ADDRESS MAP = " << global_infos->m_addr_map.size();
867 info() << " NB FUNC MAP = " << global_infos->m_func_map.size();
868 info() << " NB STACK MAP = " << global_infos->m_stack_map.size();
869 info() << " TOTAL STACK = " << global_infos->m_total_stack;
870 {
871 ProfInfos::AddrMap::const_iterator begin = global_infos->m_addr_map.begin();
872 for (; begin != global_infos->m_addr_map.end(); ++begin) {
873 Int64 nb_event = begin->second.m_counters[0];
874 total_event += nb_event;
875 Int64 nb_fp = begin->second.m_counters[2];
876 total_fp += nb_fp;
877 }
878 }
879 {
880 ProfInfos::FuncMap::const_iterator begin = global_infos->m_func_map.begin();
881 Int64 total_func_event = 0;
882 Int64 total_func_fp = 0;
883 for (; begin != global_infos->m_func_map.end(); ++begin) {
884 ProfFuncInfo* pf = begin->second;
885 Int64 nb_event = pf->m_counters[0];
886 total_func_event += nb_event;
887 Int64 nb_fp = pf->m_counters[2];
888 total_func_fp += nb_fp;
889 }
890 info() << " FUNC EVENT=" << total_func_event;
891 info() << " FUNC FP=" << total_func_fp;
892 }
893
894 if (dump_file) {
895 StringBuilder sfile_name = "profiling.addr";
896 sfile_name += process_id;
897 sfile_name += ".xml";
898 String file_name = sfile_name;
899 std::ofstream ofile(file_name.localstr());
900 ofile << "<?xml version='1.0'?>\n";
901 ofile << "<addresses>\n";
902 ProfInfos::AddrMap::const_iterator begin = global_infos->m_addr_map.begin();
903 for (; begin != global_infos->m_addr_map.end(); ++begin) {
904 void* addr = begin->first;
905 const ProfAddrInfo& pa = begin->second;
906 ProfFuncInfo* fi = pa.m_func_info;
907 if (fi) {
908 ofile << "<addr addr='" << addr << "'"
909 << " count='" << pa.m_counters[0] << "'"
910 << " fi='" << fi << "'"
911 << " fi_count='" << fi->m_counters[0] << "'"
912 << " func='" << fi->m_func_name << "'"
913 << ">\n";
914 }
915 }
916 ofile << "/<addresses>\n";
917 }
918 }
919
920 std::set<ProfFuncInfo*, ProfFuncComparer> sorted_func;
921 _sortFunctions(sorted_func);
922
923 const size_t NAME_BUF_SIZE = 8120;
924 char demangled_func_name[NAME_BUF_SIZE];
925 {
926 info() << " TOTAL EVENT = " << total_event;
927 info() << " TOTAL NB_IN_HANDLER = " << global_nb_in_handler;
928 Real nb_gflop = ((Real)total_fp * (Real)m_period) * 1e-09;
929 info() << " TOTAL FP = " << total_fp << " (nb_giga_flip=" << nb_gflop << ")";
930 info() << " RATIO FP/CYC = " << ((Real)total_fp / (Real)total_event);
931 info() << " " << Trace::Width(10) << "nb_event"
932 << " " << Trace::Width(5) << "%"
933 << " " << Trace::Width(5) << "cum%"
934 << " " << Trace::Width(12) << "event1"
935 << " " << Trace::Width(12) << "event2"
936 << " " << Trace::Width(12) << "event3"
937 << " " << " "
938 << " " << "function";
939 Integer index = 0;
940 Int64 cumulative_nb_event = 0;
941 for (ProfFuncInfo* pfi : sorted_func) {
942 const char* func_name = pfi->m_func_name;
943 const char* buf = _getDemangledName(func_name, demangled_func_name, NAME_BUF_SIZE);
944 Int64 nb_event = pfi->m_counters[0];
945 cumulative_nb_event += nb_event;
946 Int64 cumulative_total_percent = (cumulative_nb_event * 1000) / total_event;
947 Int64 total_percent = (nb_event * 1000) / total_event;
948 Int64 cumulative_percent = (cumulative_total_percent / 10);
949 Int64 percent = (total_percent / 10);
950 info() << " " << Trace::Width(10) << nb_event
951 << " " << Trace::Width(3) << percent << "." << (total_percent % 10)
952 << " " << Trace::Width(3) << cumulative_percent << "." << (cumulative_total_percent % 10)
953 << " " << Trace::Width(12) << pfi->m_counters[0]
954 << " " << Trace::Width(12) << pfi->m_counters[1]
955 << " " << Trace::Width(12) << pfi->m_counters[2]
956 << " " << pfi->m_do_stack
957 << " " << buf;
958 if (total_percent < 5 && index > 20)
959 break;
960 ++index;
961 }
962 }
963 // TODO: Calculate this information when profiling stops.
964 if (dump_file) {
965 // Create a list of stacks sorted by decreasing number of events.
966 UniqueArray<SortedProfStackInfo> sorted_stacks;
967 for (const auto& x : global_infos->m_stack_map) {
968 const ProfStackInfo& psi = x.first;
969 Int64 nb_stack = x.second;
970 sorted_stacks.add(SortedProfStackInfo(psi, nb_stack));
971 }
972 std::sort(std::begin(sorted_stacks), std::end(sorted_stacks));
973
974 String file_name = String("profiling.callstack") + platform::getProcessId();
975 std::ofstream ofile;
976 ofile.open(file_name.localstr());
977
978 for (const auto& x : sorted_stacks) {
979 const ProfStackInfo& psi = x.stackInfo();
980 Int64 nb_stack = x.nbCount();
981 if (nb_stack < 2)
982 continue;
983 ofile << " Stack nb=" << nb_stack << '\n';
984 for (Integer z = 0; z < MAX_STACK; ++z) {
985 Int32 fidx = psi.m_funcs_info_indexes[z];
986 if (fidx >= 0) {
987 ProfFuncInfo& pfi = _funcInfoFromIndex(fidx);
988 const char* func_name = pfi.m_func_name;
989 const char* buf = _getDemangledName(func_name, demangled_func_name, NAME_BUF_SIZE);
990 ofile << " " << buf << '\n';
991 }
992 }
993 }
994 ofile.close();
995 }
996}
997
998// ****************************************************************************
999// * getInfos for someone else
1000// * [0] 0xb80dd1a3ul
1001// * [1] Size
1002// * [2] total_event
1003// * [3] total_fp
1004// * [4] (Int64) int m_period
1005// * [5] (Int64) Integer index;
1006// * nb_event
1007// * total_percent
1008// * m_counters[0]: PAPI_TOT_CYC Total cycles
1009// * m_counters[1]: PAPI_RES_STL Cycles stalled on any resource
1010// * m_counters[2]: PAPI_FP_INS Floating point instructions
1011// * strlen(func_name)
1012// *
1013// *
1014// ****************************************************************************
1015void ProfInfos::
1016getInfos(Int64Array& pkt)
1017{
1018 const size_t NAME_BUF_SIZE = 8192;
1019 char demangled_func_name[NAME_BUF_SIZE];
1020 ProfInfos* global_infos = this;
1021 Int64 total_event = 1;
1022 Int64 total_fp = 1;
1023 Integer index = 0;
1024
1025 {
1026 ProfInfos::AddrMap::const_iterator begin = global_infos->m_addr_map.begin();
1027 for (; begin != global_infos->m_addr_map.end(); ++begin) {
1028 Int64 nb_event = begin->second.m_counters[0];
1029 total_event += nb_event;
1030 Int64 nb_fp = begin->second.m_counters[2];
1031 total_fp += nb_fp;
1032 }
1033 }
1034 //Real nb_gflop = ((Real)total_fp*(Real)m_period) * 1e-09;
1035 pkt.add(total_event);
1036 pkt.add(total_fp);
1037 pkt.add((Int64)m_period);
1038 info() << "[ProfInfos::getInfos] total_event=" << total_event << ", total_fp=" << total_fp << ", m_period=" << m_period;
1039
1040 std::set<ProfFuncInfo*, ProfFuncComparer> sorted_func;
1041 _sortFunctions(sorted_func);
1042
1043 // We push the index which is zero
1044 pkt.add((Int64)index);
1045
1046 // And we continue filling the array
1047 for (ProfFuncInfo* pfi : sorted_func) {
1048 const char* func_name = pfi->m_func_name;
1049 const char* buf = _getDemangledName(func_name, demangled_func_name, NAME_BUF_SIZE);
1050 Int64 nb_event = pfi->m_counters[0];
1051 pkt.add(nb_event);
1052 Int64 total_percent = (nb_event * 1000) / total_event;
1053 pkt.add(total_percent);
1054 pkt.add(pfi->m_counters[0]);
1055 pkt.add(pfi->m_counters[1]);
1056 pkt.add(pfi->m_counters[2]);
1057
1058 Int64 mx = strlen(buf);
1059 pkt.add(mx);
1060 for (Int64 i = 0; i < mx; i += 8)
1061 pkt.add(*(Int64*)&buf[i]);
1062
1063 if (total_percent < 1 && index > 16)
1064 break;
1065 ++index;
1066 }
1067 pkt[5] = (Int64)(index);
1068 info() << "[ProfInfos::getInfos] index @[4] =" << pkt[5];
1069}
1070
1071/*---------------------------------------------------------------------------*/
1072/*---------------------------------------------------------------------------*/
1073
1074void ProfInfos::
1075dumpJSON(JSONWriter& writer)
1076{
1077 // TODO: use an identifier for function names instead of
1078 // putting the method name directly.
1079
1080 ProfInfos* global_infos = this;
1081
1082 Int64 total_event = 1;
1083 for (const auto& x : global_infos->m_addr_map) {
1084 Int64 nb_event = x.second.m_counters[0];
1085 total_event += nb_event;
1086 }
1087
1088 {
1089 int process_id = platform::getProcessId();
1090 writer.write("ProcessId", (Int64)process_id);
1091 writer.write("NbAddressMap", global_infos->m_addr_map.size());
1092 writer.write("NbFuncMap", global_infos->m_func_map.size());
1093 writer.write("NbStackMap", global_infos->m_stack_map.size());
1094 writer.write("TotalStack", global_infos->m_total_stack);
1095 writer.write("TotalEvent", total_event);
1096 }
1097
1098 std::set<ProfFuncInfo*, ProfFuncComparer> sorted_func;
1099 _sortFunctions(sorted_func);
1100
1101 const size_t NAME_BUF_SIZE = 8192;
1102 char demangled_func_name[NAME_BUF_SIZE];
1103
1104 // Writes the list of referenced methods and their mangled and demangled names
1105 {
1106 writer.writeKey("Functions");
1107 writer.beginArray();
1108 for (Int32 i = 0, n = m_current_func_info; i < n; ++i) {
1109 const ProfFuncInfo& pfi = m_func_info_buffer[i];
1110 const char* func_name = pfi.m_func_name;
1111 const char* buf = _getDemangledName(func_name, demangled_func_name, NAME_BUF_SIZE);
1112
1113 {
1114 JSONWriter::Object o(writer);
1115 writer.write("Index", (Int64)pfi.index());
1116 writer.write("Name", func_name);
1117 writer.write("DemangledName", buf);
1118 }
1119 }
1120 writer.endArray();
1121 }
1122
1123 // Writes the list of methods sorted by decreasing values
1124 // of the counters.
1125 {
1126 Integer index = 0;
1127 writer.writeKey("SortedFuncTimes");
1128 writer.beginArray();
1129 for (ProfFuncInfo* pfi : sorted_func) {
1130 const char* func_name = pfi->m_func_name;
1131 const char* buf = _getDemangledName(func_name, demangled_func_name, NAME_BUF_SIZE);
1132 {
1133 JSONWriter::Object o(writer);
1134 writer.write("Index", (Int64)(pfi->index()));
1135 writer.write("Name", buf);
1136 writer.write("Events", Int64ConstArrayView(3, pfi->m_counters));
1137 }
1138 if (index > 100)
1139 break;
1140 ++index;
1141 }
1142 writer.endArray();
1143 }
1144
1145 {
1146 // Create a list of stacks sorted by decreasing number of events.
1147 UniqueArray<SortedProfStackInfo> sorted_stacks;
1148 for (const auto& x : global_infos->m_stack_map) {
1149 const ProfStackInfo& psi = x.first;
1150 Int64 nb_stack = x.second;
1151 sorted_stacks.add(SortedProfStackInfo(psi, nb_stack));
1152 }
1153 std::sort(std::begin(sorted_stacks), std::end(sorted_stacks));
1154
1155 writer.writeKey("StackMap");
1156 writer.beginArray();
1157 for (const auto& x : sorted_stacks) {
1158 const ProfStackInfo& psi = x.stackInfo();
1159 Int64 nb_stack = x.nbCount();
1160 if (nb_stack < 2)
1161 continue;
1162 {
1163 JSONWriter::Object o(writer);
1164 writer.write("Count", nb_stack);
1165 writer.write("Stacks", Int32ConstArrayView(MAX_STACK, psi.m_funcs_info_indexes));
1166
1167 writer.writeKey("StackNames");
1168 writer.beginArray();
1169 for (Integer z = 0; z < MAX_STACK; ++z) {
1170 Int32 fidx = psi.m_funcs_info_indexes[z];
1171 if (fidx >= 0) {
1172 ProfFuncInfo& pfi = _funcInfoFromIndex(fidx);
1173 const char* func_name = pfi.m_func_name;
1174 const char* buf = _getDemangledName(func_name, demangled_func_name, NAME_BUF_SIZE);
1175 writer.writeValue(buf);
1176 }
1177 else
1178 writer.writeValue(String());
1179 }
1180 writer.endArray();
1181 }
1182 }
1183 writer.endArray();
1184 }
1185}
1186
1187/*---------------------------------------------------------------------------*/
1188/*---------------------------------------------------------------------------*/
1189
1190} // End namespace Arcane
1191
1192/*---------------------------------------------------------------------------*/
1193/*---------------------------------------------------------------------------*/
Arcane configuration file.
#define ARCANE_CHECK_POINTER(ptr)
Macro returning the pointer ptr if it is not null or throwing an exception if it is null.
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Interface to retrieve call stack information.
TraceMessage info() const
Flow for an information message.
Integer len(const char *s)
Returns the length of the string s.
bool hasDotNETRuntime()
True if the code is running with the .NET runtime.
String getEnvironmentVariable(const String &name)
Environment variable named name.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Array< Int64 > Int64Array
Dynamic one-dimensional array of 64-bit integers.
Definition UtilsTypes.h:125
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
ConstArrayView< Int32 > Int32ConstArrayView
C equivalent of a 1D array of 32-bit integers.
Definition UtilsTypes.h:482
ConstArrayView< Int64 > Int64ConstArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:480
double Real
Type representing a real number.
std::int32_t Int32
Signed integer type of 32 bits.