Arcane  v3.14.10.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
MemoryInfo.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2022 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/* MemoryInfo.cc (C) 2000-2022 */
9/* */
10/* Collecteur d'informations sur l'usage mémoire. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/ArcanePrecomp.h"
15
16#include "arcane/utils/String.h"
17#include "arcane/utils/Iostream.h"
18#include "arcane/utils/FatalErrorException.h"
19#include "arcane/utils/Iterator.h"
20#include "arcane/utils/MemoryInfo.h"
21#include "arcane/utils/ITraceMng.h"
22#include "arcane/utils/IStackTraceService.h"
23#include "arcane/utils/PlatformUtils.h"
24#include "arcane/utils/ValueConvert.h"
25
26#include <vector>
27#include <algorithm>
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32// TODO: les hooks sont obsolètes car non thread-safe.
33// Il faudrait utiliser LD_PRELOAD
34// d'une bibliothèque qui surcharge malloc(), realloc(), ...,
35// puis faire un dlopen sur la libc et appeler dans notre
36// bibliothèque les routinies d'allocation de la libc.
37
38// Cette macro ARCANE_CHECK_MEMORY_USE_MALLOC_HOOK est maintenant définie
39// lors de la configuration
40// #if defined(ARCANE_OS_LINUX)
41// #define ARCANE_CHECK_MEMORY_USE_MALLOC_HOOK
42// #endif
43
44/*---------------------------------------------------------------------------*/
45/*---------------------------------------------------------------------------*/
46
47#ifdef ARCANE_CHECK_MEMORY_USE_MALLOC_HOOK
48#include <malloc.h>
49#endif
50
51/*---------------------------------------------------------------------------*/
52/*---------------------------------------------------------------------------*/
53
54namespace Arcane
55{
56
57/*---------------------------------------------------------------------------*/
58/*---------------------------------------------------------------------------*/
59
60extern "C++" MemoryInfo*
61arcaneGlobalTrueMemoryInfo()
62{
63 static MemoryInfo mem_info;
64 return &mem_info;
65}
66
67/*---------------------------------------------------------------------------*/
68/*---------------------------------------------------------------------------*/
69
70#ifdef ARCANE_CHECK_MEMORY_USE_MALLOC_HOOK
71static void*(*old_malloc_hook)(size_t,const void*);
72static void(*old_free_hook)(void*,const void*);
73static void*(*old_realloc_hook)(void* __ptr,
74 size_t __size,
75 __const void*);
76
77
78static Arcane::Int64 global_nb_malloc = 1;
79static void* my_malloc_hook(size_t size,const void* caller);
80static void my_free_hook(void* ptr,const void* caller);
81static void* my_realloc_hook(void* __ptr,
82 size_t __size,
83 __const void*);
84static void _pushHook()
85{
86 __malloc_hook = old_malloc_hook;
87 __free_hook = old_free_hook;
88 __realloc_hook = old_realloc_hook;
89}
90static void _popHook()
91{
92 __malloc_hook = my_malloc_hook;
93 __free_hook = my_free_hook;
94 __realloc_hook = my_realloc_hook;
95}
96
97static void* my_malloc_hook(size_t size,const void* /*caller*/)
98{
99 _pushHook();
100 void* r = malloc(size);
101 ++global_nb_malloc;
102 //std::cerr << "*ALLOC = " << r << " s=" << size << '\n';
103 arcaneGlobalTrueMemoryInfo()->addInfo(0,r,size);
104 arcaneGlobalTrueMemoryInfo()->checkMemory(0,size);
105 _popHook();
106 return r;
107}
108static void my_free_hook(void* ptr,const void* /*caller*/)
109{
110 _pushHook();
111 arcaneGlobalTrueMemoryInfo()->removeInfo(0,ptr,true);
112 //std::cerr << "*FREE = " << ptr << '\n';
113 ++global_nb_malloc;
114 free(ptr);
115 _popHook();
116}
117static void* my_realloc_hook(void* ptr,size_t size,const void* /*caller*/)
118{
119 _pushHook();
120 //free(ptr);
121 ++global_nb_malloc;
122 //std::cerr << "*REFREE = " << ptr << '\n';
123 arcaneGlobalTrueMemoryInfo()->removeInfo(0,ptr,true);
124 void* r = realloc(ptr,size);
125 ++global_nb_malloc;
126 //std::cerr << "*REALLOC = " << r << " s=" << size << '\n';
127 arcaneGlobalTrueMemoryInfo()->addInfo(0,r,size);
128 arcaneGlobalTrueMemoryInfo()->checkMemory(0,size);
129 _popHook();
130 return r;
131}
132
133static void _initMallocHook()
134{
135 old_malloc_hook = __malloc_hook;
136 __malloc_hook = my_malloc_hook;
137 old_free_hook = __free_hook;
138 __free_hook = my_free_hook;
139 old_realloc_hook = __realloc_hook;
140 __realloc_hook = my_realloc_hook;
141}
142
143static void _restoreMallocHook()
144{
145 __free_hook = old_free_hook;
146 __malloc_hook = old_malloc_hook;
147 __realloc_hook = old_realloc_hook;
148}
149//void (*__malloc_initialize_hook)(void) = my_init_hook;
150#else
151static void _initMallocHook()
152{
153}
154static void _restoreMallocHook()
155{
156}
157static void _pushHook()
158{
159}
160static void _popHook()
161{
162}
163#endif
164
165static bool global_check_memory = false;
166extern "C++" void
167arcaneInitCheckMemory()
168{
169 String s = platform::getEnvironmentVariable("ARCANE_CHECK_MEMORY");
170 if (!s.null()){
171 global_check_memory = true;
172 arcaneGlobalMemoryInfo()->beginCollect();
173 }
174}
175
176extern "C++" void
177arcaneExitCheckMemory()
178{
179 if (global_check_memory)
180 arcaneGlobalMemoryInfo()->endCollect();
181 global_check_memory = false;
182}
183
184/*---------------------------------------------------------------------------*/
185/*---------------------------------------------------------------------------*/
186
187MemoryInfo::
188MemoryInfo()
189: m_alloc_id(0)
190, m_max_allocated(0)
191, m_current_allocated(0)
192, m_biggest_allocated(0)
193, m_info_big_alloc(1000000)
194, m_info_biggest_minimal(2000000)
195, m_info_peak_minimal(10000000)
196, m_iteration(0)
197, m_trace(0)
198, m_display_max_alloc(true)
199, m_in_display(false)
200, m_is_first_collect(true)
201, m_is_stack_trace_active(true)
202{
203}
204
205/*---------------------------------------------------------------------------*/
206/*---------------------------------------------------------------------------*/
207
208MemoryInfo::
209~MemoryInfo()
210{
211}
212
213/*---------------------------------------------------------------------------*/
214/*---------------------------------------------------------------------------*/
215
216void MemoryInfo::
217setOwner(const void* owner,const TraceInfo& ti)
218{
219 MemoryTraceInfoMap::iterator i = m_owner_infos.find(owner);
220 if (i!=m_owner_infos.end()){
221 i->second = ti;
222 }
223}
224
225/*---------------------------------------------------------------------------*/
226/*---------------------------------------------------------------------------*/
227
228void MemoryInfo::
229addInfo(const void* owner,const void* ptr,Int64 size)
230{
231 //NOTE: Cette méthode doit être réentrente.
232 //TODO: verifier owner present.
233 MemoryInfoMap::const_iterator i = m_infos.find(ptr);
234 String stack_value;
235 //cout << "** ADD: " << ptr << '\n';
236 if (i==m_infos.end()){
237 MemoryInfoChunk c(owner,size,m_alloc_id,m_iteration);
238 //if (size>5000)
239 //std::cout << " ALLOC size=" << size << " ptr=" << ptr << '\n';
240 if (size>=m_info_big_alloc && m_is_stack_trace_active){
241 IStackTraceService* s = platform::getStackTraceService();
242 if (s){
243 stack_value = s->stackTrace(2).toString();
244 c.setStackTrace(stack_value);
245 }
246 }
247 m_infos.insert(MemoryInfoMap::value_type(ptr,c));
248 }
249 else{
250 //cout << "** OLD VALUE file=" << i->second->m_file << " line=" << i->second->m_line;
251 //if (i->second->m_name)
252 //cout << " name=" << i->second->m_name << '\n';
253 cout << "** addInfo() ALREADY IN MAP VALUE=" << ptr << " size=" << size << '\n';
254 //throw FatalErrorException("MemoryInfo::addInfo() pointer already in map");
255 }
256 m_current_allocated += size;
257 ++m_alloc_id;
258}
259
260/*---------------------------------------------------------------------------*/
261/*---------------------------------------------------------------------------*/
262
263void MemoryInfo::
264createOwner(const void* owner,const TraceInfo& trace_info)
265{
266 MemoryTraceInfoMap::iterator i = m_owner_infos.find(owner);
267 if (i==m_owner_infos.end()){
268 //cout << "** CREATE OWNER " << owner << "\n";
269 m_owner_infos.insert(MemoryTraceInfoMap::value_type(owner,trace_info));
270 }
271 else{
272 cout << "** createOwner() ALREADY IN MAP VALUE=" << owner << '\n';
273 //throw FatalErrorException("MemoryInfo::createOwner() owner already in map");
274 }
275}
276
277/*---------------------------------------------------------------------------*/
278/*---------------------------------------------------------------------------*/
279
280void MemoryInfo::
281addInfo(const void* owner,const void* ptr,Int64 size,const void* /*old_ptr*/)
282{
283 addInfo(owner,ptr,size);
284}
285
286/*---------------------------------------------------------------------------*/
287/*---------------------------------------------------------------------------*/
288
289void MemoryInfo::
290changeOwner(const void* new_owner,const void* ptr)
291{
292 MemoryTraceInfoMap::iterator i_owner = m_owner_infos.find(new_owner);
293 if (i_owner==m_owner_infos.end()){
294 cerr << "** UNKNOWN NEW OWNER " << new_owner << '\n';
295 throw FatalErrorException("MemoryInfo::changeOwner() unknown new owner");
296 }
297 if (ptr){
298 MemoryInfoMap::iterator i = m_infos.find(ptr);
299 if (i==m_infos.end()){
300 cout << "** BAD VALUE=" << ptr << '\n';
301 throw FatalErrorException("MemoryInfo::changeOwner() pointer not in map");
302 }
303 else{
304 i->second.setOwner(i_owner->first);
305 }
306 }
307}
308
309/*---------------------------------------------------------------------------*/
310/*---------------------------------------------------------------------------*/
311
312void MemoryInfo::
313_removeOwner(const void* owner)
314{
315 MemoryTraceInfoMap::iterator i = m_owner_infos.find(owner);
316 if (i!=m_owner_infos.end()){
317 //cout << "** REMOVE OWNER " << owner << "\n";
318 m_owner_infos.erase(i);
319 }
320}
321
322/*---------------------------------------------------------------------------*/
323/*---------------------------------------------------------------------------*/
324
325void MemoryInfo::
326removeOwner(const void* owner)
327{
328 _removeOwner(owner);
329}
330
331/*---------------------------------------------------------------------------*/
332/*---------------------------------------------------------------------------*/
333
334void MemoryInfo::
335removeInfo(const void* owner,const void* ptr,bool can_fail)
336{
337 if (!ptr)
338 return;
339 MemoryInfoMap::iterator i = m_infos.find(ptr);
340 //cout << "** REMOVE: " << ptr << '\n';
341 if (i==m_infos.end()){
342 if (can_fail)
343 return;
344 cout << "MemoryInfo::removeInfo() pointer not in map";
345 //throw FatalErrorException("MemoryInfo::removeInfo() pointer not in map");
346 }
347 else{
348 MemoryInfoChunk& chunk = i->second;
349 Int64 size = chunk.size();
350 //if (size>5000)
351 //std::cout << " FREE size=" << size << " ptr=" << ptr << '\n';
352 _removeMemory(owner,size);
353 m_infos.erase(i);
354 }
355}
356
357/*---------------------------------------------------------------------------*/
358/*---------------------------------------------------------------------------*/
359
361{
362 public:
363 MemoryInfoSorter() : m_size(0), m_alloc_id(-1), m_iteration(0), m_ptr(0), m_owner(0) {}
364 MemoryInfoSorter(Int64 size,Int64 alloc_id,Integer iteration, const void* ptr,
365 const void* owner,const String& stack_trace)
366 : m_size(size), m_alloc_id(alloc_id), m_iteration(iteration), m_ptr(ptr)
367 , m_owner(owner), m_stack_trace(stack_trace) {}
368 public:
369 Int64 m_size;
370 Int64 m_alloc_id;
371 Integer m_iteration;
372 const void* m_ptr;
373 const void* m_owner;
374 String m_stack_trace;
375 public:
376 bool operator<(const MemoryInfoSorter& rhs) const
377 {
378 return m_size > rhs.m_size;
379 }
380};
381
382
384{
385 public:
386 TracePrinter(const TraceInfo* ti) : m_trace_info(ti) {}
387 void print(std::ostream& o) const
388 {
389 if (m_trace_info){
390 o << " name=" << m_trace_info->name()
391 << " file=" << m_trace_info->file()
392 << " line=" << m_trace_info->line();
393 }
394 }
395 private:
396 const TraceInfo* m_trace_info;
397};
398
399std::ostream&
400operator<<(std::ostream& o,const MemoryInfo::TracePrinter& tp)
401{
402 tp.print(o);
403 return o;
404}
405
406/*---------------------------------------------------------------------------*/
407/*---------------------------------------------------------------------------*/
408
409void MemoryInfo::
410printInfos(std::ostream& ostr)
411{
412 bool is_collecting = global_check_memory;
413 // Comme _printInfos() utilise m_infos et peut allouer de la mémoire ce
414 // qui va provoquer une modification de m_infos is on est en cours
415 // de collection, on désactive les hooks le temps de l'appel.
416 if (is_collecting)
417 _pushHook();
418 try{
419 _printInfos(ostr);
420 }
421 catch(...){
422 if (is_collecting)
423 _popHook();
424 throw;
425 }
426 if (is_collecting)
427 _popHook();
428}
429
430/*---------------------------------------------------------------------------*/
431/*---------------------------------------------------------------------------*/
432
433void MemoryInfo::
434_printInfos(std::ostream& ostr)
435{
436 Int64 total_size = 0;
437 ostr << "MemoryInfos: " << m_infos.size() << '\n';
438
439 size_t nb_chunk = m_infos.size();
440 std::vector<MemoryInfoSorter> sorted_chunk;
441 sorted_chunk.reserve(nb_chunk);
442 for( const auto& i : m_infos ){
443 sorted_chunk.push_back(MemoryInfoSorter(i.second.size(),i.second.allocId(),
444 i.second.iteration(),
445 i.first,i.second.owner(),i.second.stackTrace()));
446 }
447 std::sort(sorted_chunk.begin(),sorted_chunk.end());
448
449 for( const auto& i : sorted_chunk){
450 const void* v = i.m_ptr;
451 const void* owner = i.m_owner;
452 Int64 size = i.m_size;
453 const TraceInfo* ti = 0;
454 {
455 MemoryTraceInfoMap::iterator i_owner = m_owner_infos.find(owner);
456 if (i_owner!=m_owner_infos.end()){
457 ti = &i_owner->second;
458 }
459 }
460 total_size += size;
461 if (size>=m_info_big_alloc){
462 ostr << " Remaining: " << v << " size=" << size << " id=" << i.m_alloc_id
463 << " iteration=" << i.m_iteration
464 << TracePrinter(ti) << " trace=" << i.m_stack_trace << '\n';
465 }
466 }
467 ostr << "Total size=" << total_size;
468}
469
470/*---------------------------------------------------------------------------*/
471/*---------------------------------------------------------------------------*/
472
473void MemoryInfo::
474printAllocatedMemory(std::ostream& ostr,Integer iteration)
475{
476 ostr << " INFO_ALLOCATION: current= " << m_current_allocated
477 << " ITERATION= " << iteration
478 << " NB_CHUNK=" << m_infos.size()
479 << " ID=" << m_alloc_id
480 << '\n';
481 for( ConstIterT<MemoryInfoMap> i(m_infos); i(); ++i ){
482 const MemoryInfoChunk& mi = i->second;
483 if (mi.iteration()!=iteration)
484 continue;
485 Int64 size = mi.size();
486 if (size>=m_info_big_alloc){
487 ostr << " Allocated: " << " iteration=" << iteration
488 << " size=" << size << " id=" << mi.allocId()
489 << " trace=" << mi.stackTrace() << '\n';
490 }
491 }
492 for( ConstIterT<MemoryInfoMap> i(m_infos); i(); ++i ){
493 const MemoryInfoChunk& mi = i->second;
494 if (mi.iteration()!=iteration)
495 continue;
496 Int64 size = mi.size();
497 if (size<m_info_big_alloc){
498 ostr << " Allocated: " << " iteration=" << iteration
499 << " size=" << size << " id=" << mi.allocId() << '\n';
500 }
501 }
502}
503
504/*---------------------------------------------------------------------------*/
505/*---------------------------------------------------------------------------*/
506
507void MemoryInfo::
508setTraceMng(ITraceMng* trace)
509{
510 m_trace = trace;
511}
512
513/*---------------------------------------------------------------------------*/
514/*---------------------------------------------------------------------------*/
515
516void MemoryInfo::
517beginCollect()
518{
519 if (m_is_first_collect){
520 String s = platform::getEnvironmentVariable("ARCANE_CHECK_MEMORY_BLOCK_SIZE");
521 if (!s.null()){
522 Int64 block_size = 0;
523 bool is_bad = builtInGetValue(block_size,s);
524 if (!is_bad && block_size>2){
525 m_info_big_alloc = block_size;
526 m_info_biggest_minimal = block_size * 2;
527 m_info_peak_minimal = block_size * 10;
528 }
529 if (m_trace)
530 m_trace->info() << " BLOCK SIZE '" << s;
531 }
532 m_is_first_collect = false;
533 }
534 _initMallocHook();
535}
536
537/*---------------------------------------------------------------------------*/
538/*---------------------------------------------------------------------------*/
539
540void MemoryInfo::
541endCollect()
542{
543 _restoreMallocHook();
544}
545
546/*---------------------------------------------------------------------------*/
547/*---------------------------------------------------------------------------*/
548
549bool MemoryInfo::
550isCollecting() const
551{
552 return global_check_memory;
553}
554
555/*---------------------------------------------------------------------------*/
556/*---------------------------------------------------------------------------*/
557
558void MemoryInfo::
559checkMemory(const void* owner,Int64 size)
560{
561 if (m_current_allocated>m_max_allocated){
562 m_max_allocated = m_current_allocated;
563 if (m_display_max_alloc && m_max_allocated>m_info_peak_minimal && size>5000 && m_trace && !m_in_display){
564 m_in_display = true;
565 m_trace->info() << "Memory:PEAK_MEM: iteration=" << m_iteration
566 << " max allocation reached: max="
567 << m_max_allocated << " size=" << size
568 << " id=" << m_alloc_id << " "
569 << TracePrinter(_getTraceInfo(owner));
570 m_in_display = false;
571 }
572 }
573 if (size>m_biggest_allocated){
574 m_biggest_allocated = size;
575 if (m_display_max_alloc && m_biggest_allocated>m_info_biggest_minimal && m_trace && !m_in_display){
576 m_in_display = true;
577 m_trace->info() << "Memory:PEAK_ALLOC: biggest allocation : " << size << " "
578 << " id=" << m_alloc_id << " "
579 << TracePrinter(_getTraceInfo(owner));
580 m_in_display = false;
581 }
582 }
583 if (m_info_big_alloc>0 && size>m_info_big_alloc){
584 if (m_display_max_alloc && m_trace && !m_in_display){
585 m_in_display = true;
586 String stack_value;
587 IStackTraceService* s = platform::getStackTraceService();
588 if (s){
589 stack_value= s->stackTrace(2).toString();
590 }
591#if 0
592 m_trace->info() << "Memory:BIG_ALLOC: iteration=" << m_iteration
593 << " big alloc= " << size
594 << " id=" << m_alloc_id
595 << " current=" << m_current_allocated
596 //<< " " << TracePrinter(_getTraceInfo(owner))
597 << " stack=" << stack_value;
598#endif
599 m_in_display = false;
600 }
601 }
602}
603
604/*---------------------------------------------------------------------------*/
605/*---------------------------------------------------------------------------*/
606
607void MemoryInfo::
608_removeMemory(const void* /*owner*/,Int64 size)
609{
610 m_current_allocated -= size;
611}
612
613/*---------------------------------------------------------------------------*/
614/*---------------------------------------------------------------------------*/
615
616TraceInfo* MemoryInfo::
617_getTraceInfo(const void* owner)
618{
619 MemoryTraceInfoMap::iterator i = m_owner_infos.find(owner);
620 if (i==m_owner_infos.end())
621 return 0;
622 return &i->second;
623}
624
625/*---------------------------------------------------------------------------*/
626/*---------------------------------------------------------------------------*/
627
628void MemoryInfo::
629visitAllocatedBlocks(IFunctorWithArgumentT<const MemoryInfoChunk&>* functor) const
630{
631 if (!functor)
632 return;
633 for( ConstIterT<MemoryInfoMap> i(m_infos); i(); ++i ){
634 const MemoryInfoChunk& mic = i->second;
635 functor->executeFunctor(mic);
636 }
637}
638
639/*---------------------------------------------------------------------------*/
640/*---------------------------------------------------------------------------*/
641
642extern "C++" IMemoryInfo*
643arcaneGlobalMemoryInfo()
644{
645 return arcaneGlobalTrueMemoryInfo();
646}
647
648/*---------------------------------------------------------------------------*/
649/*---------------------------------------------------------------------------*/
650
651} // End namespace Arcane
652
653/*---------------------------------------------------------------------------*/
654/*---------------------------------------------------------------------------*/
Informations sur un bloc alloué.
Definition IMemoryInfo.h:31
Interface d'un fonctor avec argument mais sans valeur de retour.
virtual void executeFunctor(ArgType arg)=0
Exécute la méthode associé
virtual StackTrace stackTrace(int first_function=0)=0
Chaîne de caractère indiquant la pile d'appel.
Interface du gestionnaire de traces.
const String & toString() const
Chaîne de caractères indiquant la pile d'appel.
Chaîne de caractères unicode.
bool null() const
Retourne true si la chaîne est nulle.
Definition String.cc:304
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
bool operator<(const Item &item1, const Item &item2)
Compare deux entités.
Definition Item.h:533
std::ostream & operator<<(std::ostream &ostr, eItemKind item_kind)
Opérateur de sortie sur un flot.