forked from esp8266/Arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathumm_malloc_cfg.h
821 lines (711 loc) · 26.1 KB
/
umm_malloc_cfg.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
/*
* Configuration for umm_malloc - target Arduino ESP8266 core
*
* Changes specific to a target platform go here.
*
* This comment section changed to below in the upstream version, keeping old method for now.
*
* Configuration for umm_malloc - DO NOT EDIT THIS FILE BY HAND!
*
* Refer to the notes below for how to configure the build at compile time
* using -D to define non-default values
*/
#ifndef _UMM_MALLOC_CFG_H
#define _UMM_MALLOC_CFG_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <pgmspace.h>
#include "../debug.h"
#include "../esp8266_undocumented.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <core_esp8266_features.h>
#include <stdlib.h>
#include <osapi.h>
#include "c_types.h"
/*
* There are a number of defines you can set at compile time that affect how
* the memory allocator will operate.
*
* Unless otherwise noted, the default state of these values is #undef-ined!
*
* If you set them via the -D option on the command line (preferred method)
* then this file handles all the configuration automagically and warns if
* there is an incompatible configuration.
*
* UMM_TEST_BUILD
*
* Set this if you want to compile in the test suite
*
* UMM_BEST_FIT (default)
*
* Set this if you want to use a best-fit algorithm for allocating new blocks.
* On by default, turned off by UMM_FIRST_FIT
*
* UMM_FIRST_FIT
*
* Set this if you want to use a first-fit algorithm for allocating new blocks.
* Faster than UMM_BEST_FIT but can result in higher fragmentation.
*
* UMM_INFO
*
* Enables a dump of the heap contents and a function to return the total
* heap size that is unallocated - note this is not the same as the largest
* unallocated block on the heap!
*
* Set if you want the ability to calculate metrics on demand
*
* UMM_INLINE_METRICS
*
* Set this if you want to have access to a minimal set of heap metrics that
* can be used to gauge heap health.
* Setting this at compile time will automatically set UMM_INFO.
* Note that enabling this define will add a slight runtime penalty.
*
* UMM_INTEGRITY_CHECK
*
* Set if you want to be able to verify that the heap is semantically correct
* before or after any heap operation - all of the block indexes in the heap
* make sense.
* Slows execution dramatically but catches errors really quickly.
*
* UMM_POISON_CHECK
*
* Set if you want to be able to leave a poison buffer around each allocation.
* Note this uses an extra 8 bytes per allocation, but you get the benefit of
* being able to detect if your program is writing past an allocated buffer.
*
* UMM_DBG_LOG_LEVEL=n
*
* Set n to a value from 0 to 6 depending on how verbose you want the debug
* log to be
*
* ----------------------------------------------------------------------------
*
* Support for this library in a multitasking environment is provided when
* you add bodies to the UMM_CRITICAL_ENTRY and UMM_CRITICAL_EXIT macros
* (see below)
*
* ----------------------------------------------------------------------------
*/
#define UMM_BEST_FIT
#define UMM_INFO
// #define UMM_INLINE_METRICS
#define UMM_STATS
/*
* To support API call, system_show_malloc(), -DUMM_INFO is required.
*
* For the ESP8266 we need an ISR safe function to call for implementing
* xPortGetFreeHeapSize(). We can get this with one of these options:
* 1) -DUMM_STATS or -DUMM_STATS_FULL
* 2) -DUMM_INLINE_METRICS (and implicitly includes -DUMM_INFO)
*
* If frequent calls are made to ESP.getHeapFragmentation(),
* -DUMM_INLINE_METRICS would reduce long periods of interrupts disabled caused
* by frequent calls to `umm_info()`. Instead, the computations get distributed
* across each malloc, realloc, and free. This appears to require an additional
* 116 bytes of IRAM vs using `UMM_STATS` with `UMM_INFO`.
*
* When both UMM_STATS and UMM_INLINE_METRICS are defined, macros and structures
* have been optimized to reduce duplications.
*
*/
#ifdef UMM_TEST_BUILD
extern char test_umm_heap[];
#endif
#ifdef UMM_TEST_BUILD
/* Start addresses and the size of the heap */
#define UMM_MALLOC_CFG_HEAP_ADDR (test_umm_heap)
#define UMM_MALLOC_CFG_HEAP_SIZE 0x10000
#else
/* Start addresses and the size of the heap */
extern char _heap_start[];
#define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0])
#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(0x3fffc000 - UMM_MALLOC_CFG_HEAP_ADDR))
#endif
/* A couple of macros to make packing structures less compiler dependent */
#define UMM_H_ATTPACKPRE
#define UMM_H_ATTPACKSUF __attribute__((__packed__))
/* -------------------------------------------------------------------------- */
#ifdef UMM_BEST_FIT
#ifdef UMM_FIRST_FIT
#error Both UMM_BEST_FIT and UMM_FIRST_FIT are defined - pick one!
#endif
#else /* UMM_BEST_FIT is not defined */
#ifndef UMM_FIRST_FIT
#define UMM_BEST_FIT
#endif
#endif
/* -------------------------------------------------------------------------- */
#ifdef UMM_INLINE_METRICS
#define UMM_FRAGMENTATION_METRIC_INIT() umm_fragmentation_metric_init()
#define UMM_FRAGMENTATION_METRIC_ADD(c) umm_fragmentation_metric_add(c)
#define UMM_FRAGMENTATION_METRIC_REMOVE(c) umm_fragmentation_metric_remove(c)
#ifndef UMM_INFO
#define UMM_INFO
#endif
#else
#define UMM_FRAGMENTATION_METRIC_INIT()
#define UMM_FRAGMENTATION_METRIC_ADD(c)
#define UMM_FRAGMENTATION_METRIC_REMOVE(c)
#endif // UMM_INLINE_METRICS
/* -------------------------------------------------------------------------- */
/*
* -D UMM_INFO :
*
* Enables a dup of the heap contents and a function to return the total
* heap size that is unallocated - note this is not the same as the largest
* unallocated block on the heap!
*/
// #define UMM_INFO
#ifdef UMM_INFO
typedef struct UMM_HEAP_INFO_t {
unsigned int totalEntries;
unsigned int usedEntries;
unsigned int freeEntries;
unsigned int totalBlocks;
unsigned int usedBlocks;
unsigned int freeBlocks;
unsigned int freeBlocksSquared;
#ifdef UMM_INLINE_METRICS
size_t oom_count;
#define UMM_OOM_COUNT ummHeapInfo.oom_count
#define UMM_FREE_BLOCKS ummHeapInfo.freeBlocks
#endif
unsigned int maxFreeContiguousBlocks;
}
UMM_HEAP_INFO;
extern UMM_HEAP_INFO ummHeapInfo;
extern ICACHE_FLASH_ATTR void *umm_info( void *ptr, bool force );
#ifdef UMM_INLINE_METRICS
extern size_t umm_free_heap_size( void );
#else
extern ICACHE_FLASH_ATTR size_t umm_free_heap_size( void );
#endif
// umm_max_block_size changed to umm_max_free_block_size in upstream.
extern ICACHE_FLASH_ATTR size_t umm_max_block_size( void );
extern ICACHE_FLASH_ATTR int umm_usage_metric( void );
extern ICACHE_FLASH_ATTR int umm_fragmentation_metric( void );
#else
#define umm_info(p,b)
#define umm_free_heap_size() (0)
#define umm_max_block_size() (0)
#define umm_fragmentation_metric() (0)
#define umm_usage_metric() (0)
#endif
/*
* -D UMM_STATS :
* -D UMM_STATS_FULL
*
* This option provides a lightweight alternative to using `umm_info` just for
* getting `umm_free_heap_size`. With this option, a "free blocks" value is
* updated on each call to malloc/free/realloc. This option does not offer all
* the information that `umm_info` would have generated.
*
* This option is good for cases where the free heap is checked frequently. An
* example is when an app closely monitors free heap to detect memory leaks. In
* this case a single-core CPUs interrupt processing would have suffered the
* most.
*
* UMM_STATS_FULL provides additional heap statistics. It can be used to gain
* additional insight into heap usage. This option would add an additional 132
* bytes of IRAM.
*
* Status: TODO: Needs to be proposed for upstream.
*/
/*
#define UMM_STATS
#define UMM_STATS_FULL
*/
#if !defined(UMM_STATS) && !defined(UMM_STATS_FULL) && !defined(UMM_INLINE_METRICS)
#define UMM_STATS
#endif
#if defined(UMM_STATS) && defined(UMM_STATS_FULL)
#undef UMM_STATS
#endif
#if defined(UMM_STATS) || defined(UMM_STATS_FULL)
typedef struct UMM_STATISTICS_t {
#ifndef UMM_INLINE_METRICS
// If we are doing UMM_INLINE_METRICS, we can move oom_count and free_blocks to
// umm_info's structure and save a little DRAM and IRAM.
// Otherwise it is defined here.
size_t free_blocks;
size_t oom_count;
#define UMM_OOM_COUNT ummStats.oom_count
#define UMM_FREE_BLOCKS ummStats.free_blocks
#endif
#ifdef UMM_STATS_FULL
size_t free_blocks_min;
size_t free_blocks_isr_min;
size_t alloc_max_size;
size_t last_alloc_size;
size_t id_malloc_count;
size_t id_malloc_zero_count;
size_t id_realloc_count;
size_t id_realloc_zero_count;
size_t id_free_count;
size_t id_free_null_count;
#endif
}
UMM_STATISTICS;
extern UMM_STATISTICS ummStats;
#ifdef UMM_INLINE_METRICS
#define STATS__FREE_BLOCKS_UPDATE(s) (void)(s)
#else
#define STATS__FREE_BLOCKS_UPDATE(s) ummStats.free_blocks += (s)
#endif
#define STATS__OOM_UPDATE() UMM_OOM_COUNT += 1
extern size_t umm_free_heap_size_lw( void );
static inline size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) {
return UMM_OOM_COUNT;
}
#else // not UMM_STATS or UMM_STATS_FULL
#define STATS__FREE_BLOCKS_UPDATE(s) (void)(s)
#define STATS__OOM_UPDATE() (void)0
#endif
#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO)
size_t ICACHE_FLASH_ATTR umm_block_size( void );
#endif
#ifdef UMM_STATS_FULL
#define STATS__FREE_BLOCKS_MIN() \
do { \
if (UMM_FREE_BLOCKS < ummStats.free_blocks_min) \
ummStats.free_blocks_min = UMM_FREE_BLOCKS; \
} while(false)
#define STATS__FREE_BLOCKS_ISR_MIN() \
do { \
if (UMM_FREE_BLOCKS < ummStats.free_blocks_isr_min) \
ummStats.free_blocks_isr_min = UMM_FREE_BLOCKS; \
} while(false)
#define STATS__ALLOC_REQUEST(tag, s) \
do { \
ummStats.tag##_count += 1; \
ummStats.last_alloc_size = s; \
if (ummStats.alloc_max_size < s) \
ummStats.alloc_max_size = s; \
} while(false)
#define STATS__ZERO_ALLOC_REQUEST(tag, s) \
do { \
ummStats.tag##_zero_count += 1; \
} while(false)
#define STATS__NULL_FREE_REQUEST(tag) \
do { \
ummStats.tag##_null_count += 1; \
} while(false)
#define STATS__FREE_REQUEST(tag) \
do { \
ummStats.tag##_count += 1; \
} while(false)
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min( void ) {
return (size_t)ummStats.free_blocks_min * umm_block_size();
}
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void ) {
ummStats.free_blocks_min = UMM_FREE_BLOCKS;
return (size_t)ummStats.free_blocks_min * umm_block_size();
}
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void ) {
return ummStats.free_blocks_min * umm_block_size();
}
static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min( void ) {
return ummStats.free_blocks_isr_min * umm_block_size();
}
static inline size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size( void ) {
return ummStats.alloc_max_size;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size( void ) {
return ummStats.last_alloc_size;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_malloc_count( void ) {
return ummStats.id_malloc_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count( void ) {
return ummStats.id_malloc_zero_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_realloc_count( void ) {
return ummStats.id_realloc_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count( void ) {
return ummStats.id_realloc_zero_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_free_count( void ) {
return ummStats.id_free_count;
}
static inline size_t ICACHE_FLASH_ATTR umm_get_free_null_count( void ) {
return ummStats.id_free_null_count;
}
#else // Not UMM_STATS_FULL
#define STATS__FREE_BLOCKS_MIN() (void)0
#define STATS__FREE_BLOCKS_ISR_MIN() (void)0
#define STATS__ALLOC_REQUEST(tag, s) (void)(s)
#define STATS__ZERO_ALLOC_REQUEST(tag, s) (void)(s)
#define STATS__NULL_FREE_REQUEST(tag) (void)0
#define STATS__FREE_REQUEST(tag) (void)0
#endif
/*
Per Devyte, the core currently doesn't support masking a specific interrupt
level. That doesn't mean it can't be implemented, only that at this time
locking is implemented as all or nothing.
https://github.com/esp8266/Arduino/issues/6246#issuecomment-508612609
So for now we default to all, 15.
*/
#ifndef DEFAULT_CRITICAL_SECTION_INTLEVEL
#define DEFAULT_CRITICAL_SECTION_INTLEVEL 15
#endif
/*
* -D UMM_CRITICAL_METRICS
*
* Build option to collect timing usage data on critical section usage in
* functions: info, malloc, realloc. Collects MIN, MAX, and number of time IRQs
* were disabled at request time. Note, for realloc MAX disabled time will
* include the time spent in calling malloc and/or free. Examine code for
* specifics on what info is available and how to access.
*
* Status: TODO: Needs to be proposed for upstream. Also should include updates
* to UMM_POISON_CHECK and UMM_INTEGRITY_CHECK to include a critical section.
*/
/*
#define UMM_CRITICAL_METRICS
*/
#if defined(UMM_CRITICAL_METRICS)
// This option adds support for gathering time locked data
typedef struct UMM_TIME_STAT_t {
uint32_t min;
uint32_t max;
uint32_t start;
uint32_t intlevel;
}
UMM_TIME_STAT;
typedef struct UMM_TIME_STATS_t UMM_TIME_STATS;
extern UMM_TIME_STATS time_stats;
bool get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size);
static inline void _critical_entry(UMM_TIME_STAT *p, uint32_t *saved_ps) {
*saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
if (0U != (*saved_ps & 0x0FU)) {
p->intlevel += 1U;
}
p->start = esp_get_cycle_count();
}
static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) {
uint32_t elapse = esp_get_cycle_count() - p->start;
if (elapse < p->min)
p->min = elapse;
if (elapse > p->max)
p->max = elapse;
xt_wsr_ps(*saved_ps);
}
#endif
//////////////////////////////////////////////////////////////////////////////////////
/*
* A couple of macros to make it easier to protect the memory allocator
* in a multitasking system. You should set these macros up to use whatever
* your system uses for this purpose. You can disable interrupts entirely, or
* just disable task switching - it's up to you
*
* NOTE WELL that these macros MUST be allowed to nest, because umm_free() is
* called from within umm_malloc()
*/
#ifdef UMM_TEST_BUILD
extern int umm_critical_depth;
extern int umm_max_critical_depth;
#define UMM_CRITICAL_ENTRY() {\
++umm_critical_depth; \
if (umm_critical_depth > umm_max_critical_depth) { \
umm_max_critical_depth = umm_critical_depth; \
} \
}
#define UMM_CRITICAL_EXIT() (umm_critical_depth--)
#else
#if defined(UMM_CRITICAL_METRICS)
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
#define UMM_CRITICAL_ENTRY(tag)_critical_entry(&time_stats.tag, &_saved_ps_##tag)
#define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag)
#else // ! UMM_CRITICAL_METRICS
// This method preserves the intlevel on entry and restores the
// original intlevel at exit.
#define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag
#define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL)
#define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag)
#endif
#endif
/*
* -D UMM_LIGHTWEIGHT_CPU
*
* The use of this macro is hardware/application specific.
*
* With some CPUs, the only available method for locking are the instructions
* for interrupts disable/enable. These macros are meant for lightweight single
* CPU systems that are sensitive to interrupts being turned off for too long. A
* typically UMM_CRITICAL_ENTRY would save current IRQ state then disable IRQs.
* Then UMM_CRITICAL_EXIT would restore previous IRQ state. This option adds
* additional critical entry/exit points by the method of defining the macros
* UMM_CRITICAL_SUSPEND and UMM_CRITICAL_RESUME to the values of
* UMM_CRITICAL_EXIT and UMM_CRITICAL_ENTRY. These additional exit/entries
* allow time to service interrupts during the reentrant sections of the code.
*
* Performance may be impacked if used with multicore CPUs. The higher frquency
* of locking and unlocking may be an issue with locking methods that have a
* high overhead.
*
* Status: TODO: Needs to be proposed for upstream.
*/
/*
*/
#define UMM_LIGHTWEIGHT_CPU
#ifdef UMM_LIGHTWEIGHT_CPU
#define UMM_CRITICAL_SUSPEND(tag) UMM_CRITICAL_EXIT(tag)
#define UMM_CRITICAL_RESUME(tag) UMM_CRITICAL_ENTRY(tag)
#else
#define UMM_CRITICAL_SUSPEND(tag) do {} while(0)
#define UMM_CRITICAL_RESUME(tag) do {} while(0)
#endif
/*
* -D UMM_REALLOC_MINIMIZE_COPY or
* -D UMM_REALLOC_DEFRAG
*
* Pick one of these two stratagies. UMM_REALLOC_MINIMIZE_COPY grows upward or
* shrinks an allocation, avoiding copy when possible. UMM_REALLOC_DEFRAG gives
* priority with growing the revised allocation toward an adjacent hole in the
* direction of the beginning of the heap when possible.
*
* Status: TODO: These are new options introduced to optionally restore the
* previous defrag propery of realloc. The issue has been raised in the upstream
* repo. No response at this time. Based on response, may propose for upstream.
*/
/*
#define UMM_REALLOC_MINIMIZE_COPY
*/
#define UMM_REALLOC_DEFRAG
/*
* -D UMM_INTEGRITY_CHECK :
*
* Enables heap integrity check before any heap operation. It affects
* performance, but does NOT consume extra memory.
*
* If integrity violation is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
*
* Note that not all buffer overruns are detected: each buffer is aligned by
* 4 bytes, so there might be some trailing "extra" bytes which are not checked
* for corruption.
*/
/*
* Not normally enabled. Full intergity check may exceed 10us.
*/
/*
#define UMM_INTEGRITY_CHECK
*/
#ifdef UMM_INTEGRITY_CHECK
extern bool umm_integrity_check( void );
# define INTEGRITY_CHECK() umm_integrity_check()
extern void umm_corruption(void);
# define UMM_HEAP_CORRUPTION_CB() DBGLOG_FUNCTION( "Heap Corruption!" )
#else
# define INTEGRITY_CHECK() (1)
#endif
/////////////////////////////////////////////////
/*
* -D UMM_POISON_CHECK :
* -D UMM_POISON_CHECK_LITE
*
* Enables heap poisoning: add predefined value (poison) before and after each
* allocation, and check before each heap operation that no poison is
* corrupted.
*
* Other than the poison itself, we need to store exact user-requested length
* for each buffer, so that overrun by just 1 byte will be always noticed.
*
* Customizations:
*
* UMM_POISON_SIZE_BEFORE:
* Number of poison bytes before each block, e.g. 4
* UMM_POISON_SIZE_AFTER:
* Number of poison bytes after each block e.g. 4
* UMM_POISONED_BLOCK_LEN_TYPE
* Type of the exact buffer length, e.g. `uint16_t`
*
* NOTE: each allocated buffer is aligned by 4 bytes. But when poisoning is
* enabled, actual pointer returned to user is shifted by
* `(sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE)`.
* It's your responsibility to make resulting pointers aligned appropriately.
*
* If poison corruption is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
*
* UMM_POISON_CHECK - does a global heap check on all active allocation at
* every alloc API call. May exceed 10us due to critical section with IRQs
* disabled.
*
* UMM_POISON_CHECK_LITE - checks the allocation presented at realloc()
* and free(). Expands the poison check on the current allocation to
* include its nearest allocated neighbors in the heap.
* umm_malloc() will also checks the neighbors of the selected allocation
* before use.
*
* Status: TODO?: UMM_POISON_CHECK_LITE is a new option. We could propose for
* upstream; however, the upstream version has much of the framework for calling
* poison check on each alloc call refactored out. Not sure how this will be
* received.
*/
/*
* Compatibility for deprecated UMM_POISON
*/
#if defined(UMM_POISON) && !defined(UMM_POISON_CHECK)
#define UMM_POISON_CHECK_LITE
#endif
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_CORE)
#if !defined(UMM_POISON_CHECK) && !defined(UMM_POISON_CHECK_LITE)
/*
#define UMM_POISON_CHECK
*/
#define UMM_POISON_CHECK_LITE
#endif
#endif
#define UMM_POISON_SIZE_BEFORE (4)
#define UMM_POISON_SIZE_AFTER (4)
#define UMM_POISONED_BLOCK_LEN_TYPE uint32_t
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
extern void *umm_poison_malloc( size_t size );
extern void *umm_poison_calloc( size_t num, size_t size );
extern void *umm_poison_realloc( void *ptr, size_t size );
extern void umm_poison_free( void *ptr );
extern bool umm_poison_check( void );
// Local Additions to better report location in code of the caller.
void *umm_poison_realloc_fl( void *ptr, size_t size, const char* file, int line );
void umm_poison_free_fl( void *ptr, const char* file, int line );
#if defined(UMM_POISON_CHECK_LITE)
/*
* We can safely do individual poison checks at free and realloc and stay
* under 10us or close.
*/
# define POISON_CHECK() 1
# define POISON_CHECK_NEIGHBORS(c) \
do {\
if(!check_poison_neighbors(c)) \
panic();\
} while(false)
#else
/* Not normally enabled. A full heap poison check may exceed 10us. */
# define POISON_CHECK() umm_poison_check()
# define POISON_CHECK_NEIGHBORS(c) do{}while(false)
#endif
#else
# define POISON_CHECK() 1
# define POISON_CHECK_NEIGHBORS(c) do{}while(false)
#endif
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
/*
* Overhead adjustments needed for free_blocks to express the number of bytes
* that can actually be allocated.
*/
#define UMM_OVERHEAD_ADJUST ( \
umm_block_size()/2 + \
UMM_POISON_SIZE_BEFORE + \
UMM_POISON_SIZE_AFTER + \
sizeof(UMM_POISONED_BLOCK_LEN_TYPE))
#else
#define UMM_OVERHEAD_ADJUST (umm_block_size()/2)
#endif
/////////////////////////////////////////////////
#undef DBGLOG_FUNCTION
#undef DBGLOG_FUNCTION_P
#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_OOM) || \
defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || \
defined(UMM_INTEGRITY_CHECK)
#define DBGLOG_FUNCTION(fmt, ...) ets_uart_printf(fmt, ##__VA_ARGS__)
#else
#define DBGLOG_FUNCTION(fmt, ...) do { (void)fmt; } while(false)
#endif
/////////////////////////////////////////////////
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK)
#if !defined(DBGLOG_LEVEL) || DBGLOG_LEVEL < 3
// All debug prints in UMM_POISON_CHECK are level 3
#undef DBGLOG_LEVEL
#define DBGLOG_LEVEL 3
#endif
#endif
#if defined(UMM_CRITICAL_METRICS)
struct UMM_TIME_STATS_t {
UMM_TIME_STAT id_malloc;
UMM_TIME_STAT id_realloc;
UMM_TIME_STAT id_free;
#ifdef UMM_INFO
UMM_TIME_STAT id_info;
#endif
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
UMM_TIME_STAT id_poison;
#endif
#ifdef UMM_INTEGRITY_CHECK
UMM_TIME_STAT id_integrity;
#endif
UMM_TIME_STAT id_no_tag;
};
#endif
/////////////////////////////////////////////////
#ifdef DEBUG_ESP_OOM
#define MEMLEAK_DEBUG
// umm_*alloc are not renamed to *alloc
// Assumes umm_malloc.h has already been included.
#define umm_zalloc(s) umm_calloc(1,s)
void* malloc_loc (size_t s, const char* file, int line);
void* calloc_loc (size_t n, size_t s, const char* file, int line);
void* realloc_loc (void* p, size_t s, const char* file, int line);
// *alloc are macro calling *alloc_loc calling+checking umm_*alloc()
// they are defined at the bottom of this file
/////////////////////////////////////////////////
#elif defined(UMM_POISON_CHECK)
void* realloc_loc (void* p, size_t s, const char* file, int line);
void free_loc (void* p, const char* file, int line);
#else // !defined(ESP_DEBUG_OOM)
#endif
#ifdef __cplusplus
}
#endif
#endif /* _UMM_MALLOC_CFG_H */
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DEBUG_ESP_OOM
// this must be outside from "#ifndef _UMM_MALLOC_CFG_H"
// because Arduino.h's <cstdlib> does #undef *alloc
// Arduino.h recall us to redefine them
#include <pgmspace.h>
// Reuse pvPort* calls, since they already support passing location information.
void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line);
void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line);
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line);
void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line);
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line);
#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortMalloc(s, mem_debug_file, __LINE__); })
#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortCalloc(n, s, mem_debug_file, __LINE__); })
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortRealloc(p, s, mem_debug_file, __LINE__); })
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; vPortFree(p, mem_debug_file, __LINE__); })
#else
#define dbg_heap_free(p) free(p)
#endif
#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
#include <pgmspace.h>
void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line);
#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortRealloc(p, s, mem_debug_file, __LINE__); })
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line);
//C - to be discussed
/*
Problem, I would like to report the file and line number with the umm poison
event as close as possible to the event. The #define method works for malloc,
calloc, and realloc those names are not as generic as free. A #define free
captures too much. Classes with methods called free are included :(
Inline functions would report the address of the inline function in the .h
not where they are called.
Anybody know a trick to make this work?
Create dbg_heap_free() as an alternative for free() when you need a little
more help in debugging the more challenging problems.
*/
#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; vPortFree(p, mem_debug_file, __LINE__); })
#else
#define dbg_heap_free(p) free(p)
#endif /* DEBUG_ESP_OOM */
#ifdef __cplusplus
}
#endif