forked from esp8266/Arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNotes.h
356 lines (278 loc) · 15.8 KB
/
Notes.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
#if 0
/*
* This .h is nothing but comments about thoughts and observations made while
* updating the Arduino ESP8266 Core, with the new upstream umm_malloc. It is
* added as a .h so that it does not get lost and to avoid cluttering up the
* code with a huge block comment.
PR text description:
upstream version of `umm_malloc` customized for Arduino ESP8266 Core
This updates the heap management library, umm_malloc, to the current upstream
version at https://github.com/rhempel/umm_malloc. Some reorganizing and new code
was needed to use the new version.
This is a list of noteworthy changes:
UMM_POISON - now has a lite option as well as the previous intensive check
option. The code for running the full poison test at the call of the various
alloc functions was removed in the upstream version. In this port, the missing
code was added to heap.cpp and umm_local.cpp.
* UMM_POISON - appears to have been partially changed to UMM_POISON_CHECK,
I treat it as deprecated and used UMM_POISON_CHECK when needed.
However, the Arduino Core's references to UMM_POISON were replaced with
UMM_POISON_CHECK_LITE.
* UMM_POISON_CHECK_LITE - Less intense, it just checks poison on active
neighboring allocations.
* UMM_POISON_CHECK - Full heap intensive check of poison
A cautionary note, on the use of UMM_INTEGRITY_CHECK and UMM_POISON_CHECK, and
umm_info(). All of these run with IRQs disabled, for periods that can go into
100's of us. With umm_info(NULL, true) that may go into seconds, depending on
the serial interface speed and the number of memory allocations present. Use
UMM_INTEGRITY_CHECK, UMM_POISON_CHECK, and umm_info() sparingly. If you want to
see numbers for the disabled time, explore using UMM_CRITICAL_METRICS in
umm_malloc_cfg.h.
===============================================================================
New upstream umm_malloc feature delta's from the old umm_malloc we were using:
umm_posion check for a given *alloc - failure - no longer panics.
option to run full poison check at each *alloc call, not present
option to run full interity check at each *alloc call, not present
upstream code does not call panic from poison_check_block.
Defragmenting effect of realloc is gone. It now minimizes copy. This
may have been an accident during code cleanup.
In one form or another these features have been restored in the
reintegration of the upstream umm_malloc into the Arduino ESP8266 Core.
===============================================================================
A list of changes made for local adaptation of newer upstream umm_malloc.
In umm_malloc.c
Renamed to umm_malloc.cpp
Added `extern "C" { ... };` around code.
Surround DBGLOG_LEVEL with #ifndef... Define value of DBGLOG_LEVEL from
umm_malloc_cfg.h
umm_realloc() - Added UMM_CRITICAL_SUSPEND()/UMM_CRITICAL_RESUME() for when
lightweight locks are available. eg. sti/cli. Single threaded single CPU
case.
umm_realloc() - appears to have been refactored to minimize memmove and
memcpy. The old version would always combine an adjacent block in the
direction of the start of the heap when available and do a memmove. This
had a defragging effect. This appears to have been replaced with an attempt
to minimize copy when possible.
Added heap stats tracking.
In umm_info.c
umm_info() - Added UMM_CRITICAL_DECL(id_info), updated critical sections
with tag.
Carried forward: Added NULL ptr check at beginning (umm_malloc.c).
In umm_poison.c:
Resolved C++ compiler error reported on get_poisoned(), and get_unpoisoned().
They now take in void * arg instead of unsigned char *.
Added #if ... || defined(UMM_POISON_CHECK_LITE) to the conditional.
In umm_integrity.c:
Replaced printf with DBGLOG_FUNCTION. This needs to be a malloc free
function and ISR safe.
Added critical sections.
In umm_malloc_cfg.h:
Added macro UMM_CRITICAL_SUSPEND()/UMM_CRITICAL_RESUME()
Globally change across all files %i to %d: umm_info.c, umm_malloc.c,
Added a #ifdef BUILD_UMM_MALLOC_C fence to prevent Arduino IDE from building
the various .c files that are #included into umm_malloc.cpp. They are
normally enabled by #define <feature name> in umm_malloc_cfg.h. In this
case it builds fine; however, if the define is global, the IDE will try and
build the .c by itself.
Notes,
umm_integrity_check() is called by macro INTEGRITY_CHECK which returns 1
on success. No corruption. Does a time consuming scan of the whole heap.
It will call UMM_HEAP_CORRUPTION_CB if an error is found.
umm_poison_check(), formerly known as check_poison_all_blocks(),
is called by macro POISON_CHECK which returns 1 on success for no
corruption. Does a time consuming scan of all active allocations for
modified poison. The new upstream version does *NOT* call
UMM_HEAP_CORRUPTION_CB if an error is found. The option description says
it does!
umm_poison_realloc() and umm_poison_free() no longer call the macro
UMM_HEAP_CORRUPTION_CB on poison error. Just a printf message is
generated. I have added alternative functions umm_poison_free_fl,
umm_poison_realloc_fl, and get_unpoisoned_check_neighbors in
umm_local.cpp. These expand the poison check on the current allocation to
include its nearest allocated neighbors in the heap.
umm_malloc() has been extended to call check_poison_neighbors for the
allocation it selects, conditionally for UMM_POISON_CHECK_LITE.
For upstream umm_malloc "# define POISON_CHECK() 0" should have been 1
add to list to report.
==============================================================================
Notes from searching for the best print option
Printing from the malloc routines is tricky. Since a print library
might call *alloc. Then recursion may follow as each error call may fail
into another error and so on.
Objective: To be able to print "last gasp" diagnostic messages
when interrupts are disabled and w/o availability of heap resources.
It turns out things are more complicated than that. These are three cases for
printing from the heap and the current solution.:
1. Printing detailed heap info through `umm_info(NULL, 1);`. This function
resides in flash and can only be called from non-ISR context. It can use
PROGMEM strings. Because SPI bus will not be busy when called from foreground.
At this time it is believed that, while running from foreground, a cache-miss
at INTLEVEL 15 can be handled. The key factor being the SPI bus is not
busy at the time of the cache-miss. It is not clear what gets invoked to
process the cache-miss. A software vector call? A hardware assisted transfer?
In any case `umm_info_safe_printf_P()` is also in flash.
The focus here is to print w/o allocating memory and use strings
in flash to preserve DRAM.
2. Printing diagnostic messages possibly from from ISR context.
Use `ets_uart_printf()` in boot ROM.
3. Printing diagnostic messages from `heap.cpp` these printf's need to check
`system_get_os_print()` to confirm debug-output is enabled just as
`os_printf()` did.
Do test calls to `system_get_os_print()` and call `ets_uart_printf()`
in boot ROM when debug print allowed.
Considerations:
* can be called from ISR
* can be called from malloc code, cannot use malloc
* can be called from malloc code that was called from an ISR
* can be called from within a critical section, eg. xt_rsil(15);
* this may be effectively the same as being called from an ISR?
Update: Current thinking is that from foreground we have more leeway
than an ISR.
Knowns:
* ets_printf - For RTOS SDK they replaced this function with one in the SDK.
Most of the problems I can see with ets_printf center around not being
able to maintain a port to thread context. That is you cannot have one
thread using one port while another thread uses the other. In the no OS
case we cannot have one area of code using one port and another area of
code using the other port. Most of the ROM printf functions are not built
to support this kind of usage. Things get especially dangerous when you
try to use the ets_external_printf stuff.
* ets_vprintf - by itself is safe.
* newlibc printf - not safe - lives in flash.
* newlibc snprintf - not safe - lives in flash.
* builtin putc1 print function - Is installed when you use
ets_install_uart_printf. Which calls ets_install_putc1. The selection of UART
is performed by calling uart_buff_switch with 0 for UART0 and 1 for UART1.
This should work for our purpose here, if handled as follows:
* call uart_buff_switch at each printf call to reselect UART
* Update: uart_buff_switch is now updated by uart_set_debug() in uart.cpp
* use a stack buffer to hold a copy the PROGMEM string to print from.
* use ets_vprintf for printing with putc1 function.
* os_printf_plus looks interesting. It is in IRAM. If no heap is available it
will use up to 64 bytes of stack space to copy a PROGMEM fmt for printing.
Issues:
* Printing is turned off by system_set_os_print
* putc1 needs to be in IRAM - this is a uart.cpp issue
* Need to force system_get_free_heap_size to return 0 during critical periods.
* won't work for umm_info if it prints over 64 characters.
* along with umm_info there are other debug messages that exceed 64 characters.
* ets_uart_printf - Appears safe. Just no PROGMEM support. Uses
uart_buff_switch to select UART.
===============================================================================
heap.cpp is the entry point for most of the heap API calls.
It is a merge point for abstracted heap API calls, such as _malloc_r,
pvPortMalloc, and malloc. Thin wrappers are created here for these entry points
and others. The wrappers call through to equivalent umm_malloc entry-point.
These wrappers also provide the access points to do debug options, like OOM,
Integrity Check, and Poison Check.
-DEBUG_ESP_OOM or select `Debug Level: "OOM"` from the IDE.
This option will add extra code to save information on the last OOM event. If
your code is built with the `Debug port: "Serial"` option, debug messages will
print on OOM events. You do not have to do `Debug port: "Serial"` to get OOM
debug messages. From within your code, you can make a call to
`Serial.debugOutput(true);` to enable OOM printing. Of course for this to work
your code must be built with `Debug Level: "OOM"` or equal.
-DUUM_POISON is now the same as -DUMM_POISON_CHECK_LITE
This is new behavior with this updated. 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 check the neighbors of the selected allocation before
use.
For more details and options search on UMM_POISON_CHECK in `umm_malloc_cfg.h`
TODO: provide some interesting numbers on the time to perform:
* UMM_POISON_CHECK
* UMM_INTEGRITY_CHECK
* umm_info(NUll, 0) built with and without print capability
* umm_info(NUll, 1) printing a report to Serial device.
===============================================================================
Enhancement ideas:
1. Add tagging to heap allocations. Redefine UMM_POISONED_BLOCK_LEN_TYPE,
expand it to include an element for the calling address of allocating
requester. Expand umm_info(NULL, 1) to print the respective address with each
active allocation. The difficulty here will be the ever-growing complexity of
overlapping build options. I think it would be easiest to build this in with
and expand the UMM_POISON_CHECK_LITE option.
2. A build option to not have printing, from umm_info() compiled in. This can
save on the execution time spent with interrupts disabled.
*/
/*
Dec 29, 2021
Upstream umm_malloc at git hash id 4dac43c3be7a7470dd669323021ba238081da18e
processed all project files with the style program uncrustify.
This PR updates our ported version of umm_malloc processed with "uncrustify".
This should make subsequent merges of upstream into this port easier.
This also makes the style more consistant through umm_malloc.
Some edits to source files was needed to get uncrustify to work.
1) macros with "if"s need to be of the form "if ( blah ) { } " curley braces
are needed for it to parse correctly
2) These "#ifdef __cplusplus" also had to be commented out while running to
avoid parser confusion.
```
#ifdef __cplusplus
extern "C" {
#endif
```
and
```
#ifdef __cplusplus
}
#endif
```
*/
/*
Sep 26, 2022
History/Overview
ESP.getFreeHeap() needs a function it can call for free Heap size. The legacy
method was the SDK function `system_get_free_heap_size()` which is in IRAM.
`system_get_free_heap_size()` calls `xPortGetFreeHeapSize()` to get free heap
size. Our old legacy implementation used umm_info(), employing a
time-consuming method for getting free Heap size and runs with interrupts
blocked.
Later we used a distributed method that maintained the free heap size with
each malloc API call that changed the Heap. (enabled by build option UMM_STATS
or UMM_STATS_FULL) We used an internally function `umm_free_heap_size_lw()` to
report free heap size. We satisfied the requirements for
`xPortGetFreeHeapSize()` with an alias to `umm_free_heap_size_lw()`
in replacement for the legacy umm_info() call wrapper.
The upstream umm_malloc later implemented a similar method enabled by build
option UMM_INLINE_METRICS and introduced the function `umm_free_heap_size()`.
The NONOS SDK alloc request must use the DRAM Heap. Need to Ensure DRAM Heap
results when multiple Heap support is enabled. Since the SDK uses portable
malloc calls pvPortMalloc, ... we leveraged that for a solution - force
pvPortMalloc, ... APIs to serve DRAM only.
In an oversight, `xPortGetFreeHeapSize()` was left reporting the results for
the current heap selection via `umm_free_heap_size_lw()`. Thus, if an SDK
function like os_printf_plus were called when the current heap selection was
IRAM, it would get the results for the IRAM Heap. Then would receive DRAM with
an alloc request. However, when the free IRAM size is too small, it would
skip the Heap alloc request and use stack space.
Solution
The resolution is to rely on build UMM_STATS(default) or UMM_STATS_FULL for
free heap size information. When not available in the build, fallback to the
upstream umm_malloc's `umm_free_heap_size()` and require the build option
UMM_INLINE_METRICS. Otherwise, fail the build.
Use function name `umm_free_heap_size_lw()` to support external request for
current heap size. When build options result in fallback using umm_info.c,
ensure UMM_INLINE_METRICS enabled and alias to `umm_free_heap_size()`.
For the multiple Heap case, `xPortGetFreeHeapSize()` becomes a unique function
and reports only DRAM free heap size. Now `system_get_free_heap_size()` will
always report DRAM free Heap size. This might be a breaking change.
Specifics:
* Support `umm_free_heap_size_lw()` as an `extern`.
* When the build options UMM_STATS/UMM_STATS_FULL are not used, fallback to
the upstream umm_malloc's `umm_free_heap_size()` function in umm_info.c
* require the UMM_INLINE_METRICS build option.
* assign `umm_free_heap_size_lw()` as an alias to `umm_free_heap_size()`
* `xPortGetFreeHeapSize()`
* For single heap builds, alias to `umm_free_heap_size_lw()`
* For multiple Heaps builds, add a dedicated function that always reports
DRAM results.
April 22, 2023
The umm_poison logic runs outside the UMM_CRITICAL_* umbrella. When interrupt
routines do alloc calls, it is possible to interrupt an in-progress allocation
just before the poison is set, with a new alloc request resulting in a false
"poison check fail" against the in-progress allocation. The SDK does mallocs
from ISRs. SmartConfig can illustrate this issue.
Move get_poisoned() within UMM_CRITICAL_* in umm_malloc() and umm_realloc().
*/
#endif