| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  | // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <sdkconfig.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define HEAP_TRACE_SRCFILE /* don't warn on inclusion here */
 | 
					
						
							|  |  |  | #include "esp_heap_trace.h"
 | 
					
						
							|  |  |  | #undef HEAP_TRACE_SRCFILE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "esp_attr.h"
 | 
					
						
							|  |  |  | #include "freertos/FreeRTOS.h"
 | 
					
						
							|  |  |  | #include "freertos/task.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-05 17:29:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  | #define STACK_DEPTH CONFIG_HEAP_TRACING_STACK_DEPTH
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 20:29:47 +03:00
										 |  |  | #if CONFIG_HEAP_TRACING_STANDALONE
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  | static portMUX_TYPE trace_mux = portMUX_INITIALIZER_UNLOCKED; | 
					
						
							|  |  |  | static bool tracing; | 
					
						
							|  |  |  | static heap_trace_mode_t mode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Buffer used for records, starting at offset 0
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | static heap_trace_record_t *buffer; | 
					
						
							|  |  |  | static size_t total_records; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Count of entries logged in the buffer.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    Maximum total_records | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | static size_t count; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Actual number of allocations logged */ | 
					
						
							|  |  |  | static size_t total_allocations; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Actual number of frees logged */ | 
					
						
							|  |  |  | static size_t total_frees; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Has the buffer overflowed and lost trace entries? */ | 
					
						
							|  |  |  | static bool has_overflowed = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (tracing) { | 
					
						
							|  |  |  |         return ESP_ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     buffer = record_buffer; | 
					
						
							|  |  |  |     total_records = num_records; | 
					
						
							|  |  |  |     memset(buffer, 0, num_records * sizeof(heap_trace_record_t)); | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t heap_trace_start(heap_trace_mode_t mode_param) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (buffer == NULL || total_records == 0) { | 
					
						
							|  |  |  |         return ESP_ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-12 20:29:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 21:41:58 +08:00
										 |  |  |     portENTER_CRITICAL(&trace_mux); | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     tracing = false; | 
					
						
							|  |  |  |     mode = mode_param; | 
					
						
							|  |  |  |     count = 0; | 
					
						
							|  |  |  |     total_allocations = 0; | 
					
						
							|  |  |  |     total_frees = 0; | 
					
						
							|  |  |  |     has_overflowed = false; | 
					
						
							|  |  |  |     heap_trace_resume(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 21:41:58 +08:00
										 |  |  |     portEXIT_CRITICAL(&trace_mux); | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static esp_err_t set_tracing(bool enable) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (tracing == enable) { | 
					
						
							|  |  |  |         return ESP_ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     tracing = enable; | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t heap_trace_stop(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return set_tracing(false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t heap_trace_resume(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return set_tracing(true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | size_t heap_trace_get_count(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t heap_trace_get(size_t index, heap_trace_record_t *record) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (record == NULL) { | 
					
						
							|  |  |  |         return ESP_ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     esp_err_t result = ESP_OK; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 21:41:58 +08:00
										 |  |  |     portENTER_CRITICAL(&trace_mux); | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  |     if (index >= count) { | 
					
						
							|  |  |  |         result = ESP_ERR_INVALID_ARG; /* out of range for 'count' */ | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         memcpy(record, &buffer[index], sizeof(heap_trace_record_t)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 21:41:58 +08:00
										 |  |  |     portEXIT_CRITICAL(&trace_mux); | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  |     return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void heap_trace_dump(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     size_t delta_size = 0; | 
					
						
							|  |  |  |     size_t delta_allocs = 0; | 
					
						
							|  |  |  |     printf("%u allocations trace (%u entry buffer)\n", | 
					
						
							|  |  |  |            count, total_records); | 
					
						
							|  |  |  |     size_t start_count = count; | 
					
						
							|  |  |  |     for (int i = 0; i < count; i++) { | 
					
						
							|  |  |  |         heap_trace_record_t *rec = &buffer[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (rec->address != NULL) { | 
					
						
							|  |  |  |             printf("%d bytes (@ %p) allocated CPU %d ccount 0x%08x caller ", | 
					
						
							|  |  |  |                    rec->size, rec->address, rec->ccount & 1, rec->ccount & ~3); | 
					
						
							|  |  |  |             for (int j = 0; j < STACK_DEPTH && rec->alloced_by[j] != 0; j++) { | 
					
						
							|  |  |  |                 printf("%p%s", rec->alloced_by[j], | 
					
						
							|  |  |  |                        (j < STACK_DEPTH - 1) ? ":" : ""); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (mode != HEAP_TRACE_ALL || STACK_DEPTH == 0 || rec->freed_by[0] == NULL) { | 
					
						
							|  |  |  |                 delta_size += rec->size; | 
					
						
							|  |  |  |                 delta_allocs++; | 
					
						
							|  |  |  |                 printf("\n"); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 printf("\nfreed by "); | 
					
						
							|  |  |  |                 for (int j = 0; j < STACK_DEPTH; j++) { | 
					
						
							|  |  |  |                     printf("%p%s", rec->freed_by[j], | 
					
						
							|  |  |  |                            (j < STACK_DEPTH - 1) ? ":" : "\n"); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (mode == HEAP_TRACE_ALL) { | 
					
						
							|  |  |  |         printf("%u bytes alive in trace (%u/%u allocations)\n", | 
					
						
							|  |  |  |                delta_size, delta_allocs, heap_trace_get_count()); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         printf("%u bytes 'leaked' in trace (%u allocations)\n", delta_size, delta_allocs); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     printf("total allocations %u total frees %u\n", total_allocations, total_frees); | 
					
						
							|  |  |  |     if (start_count != count) { // only a problem if trace isn't stopped before dumping
 | 
					
						
							|  |  |  |         printf("(NB: New entries were traced while dumping, so trace dump may have duplicate entries.)\n"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (has_overflowed) { | 
					
						
							|  |  |  |         printf("(NB: Buffer has overflowed, so trace data is incomplete.)\n"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Add a new allocation to the heap trace records */ | 
					
						
							|  |  |  | static IRAM_ATTR void record_allocation(const heap_trace_record_t *record) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-12-12 20:29:47 +03:00
										 |  |  |     if (!tracing || record->address == NULL) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 21:41:58 +08:00
										 |  |  |     portENTER_CRITICAL(&trace_mux); | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  |     if (tracing) { | 
					
						
							|  |  |  |         if (count == total_records) { | 
					
						
							|  |  |  |             has_overflowed = true; | 
					
						
							|  |  |  |             /* Move the whole buffer back one slot.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                This is a bit slow, compared to treating this buffer as a ringbuffer and rotating a head pointer. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                However, ringbuffer code gets tricky when we remove elements in mid-buffer (for leak trace mode) while | 
					
						
							|  |  |  |                trying to keep track of an item count that may overflow. | 
					
						
							|  |  |  |             */ | 
					
						
							|  |  |  |             memmove(&buffer[0], &buffer[1], sizeof(heap_trace_record_t) * (total_records -1)); | 
					
						
							|  |  |  |             count--; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // Copy new record into place
 | 
					
						
							|  |  |  |         memcpy(&buffer[count], record, sizeof(heap_trace_record_t)); | 
					
						
							|  |  |  |         count++; | 
					
						
							|  |  |  |         total_allocations++; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 21:41:58 +08:00
										 |  |  |     portEXIT_CRITICAL(&trace_mux); | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // remove a record, used when freeing
 | 
					
						
							|  |  |  | static void remove_record(int index); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* record a free event in the heap trace log
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    For HEAP_TRACE_ALL, this means filling in the freed_by pointer. | 
					
						
							|  |  |  |    For HEAP_TRACE_LEAKS, this means removing the record from the log. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | static IRAM_ATTR void record_free(void *p, void **callers) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-12-12 20:29:47 +03:00
										 |  |  |     if (!tracing || p == NULL) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 21:41:58 +08:00
										 |  |  |     portENTER_CRITICAL(&trace_mux); | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  |     if (tracing && count > 0) { | 
					
						
							|  |  |  |         total_frees++; | 
					
						
							|  |  |  |         /* search backwards for the allocation record matching this free */ | 
					
						
							|  |  |  |         int i; | 
					
						
							|  |  |  |         for (i = count - 1; i >= 0; i--) { | 
					
						
							|  |  |  |             if (buffer[i].address == p) { | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (i >= 0) { | 
					
						
							|  |  |  |             if (mode == HEAP_TRACE_ALL) { | 
					
						
							|  |  |  |                 memcpy(buffer[i].freed_by, callers, sizeof(void *) * STACK_DEPTH); | 
					
						
							|  |  |  |             } else { // HEAP_TRACE_LEAKS
 | 
					
						
							|  |  |  |                 // Leak trace mode, once an allocation is freed we remove it from the list
 | 
					
						
							|  |  |  |                 remove_record(i); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 21:41:58 +08:00
										 |  |  |     portEXIT_CRITICAL(&trace_mux); | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* remove the entry at 'index' from the ringbuffer of saved records */ | 
					
						
							|  |  |  | static IRAM_ATTR void remove_record(int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (index < count - 1) { | 
					
						
							|  |  |  |         // Remove the buffer entry from the list
 | 
					
						
							|  |  |  |         memmove(&buffer[index], &buffer[index+1], | 
					
						
							|  |  |  |                 sizeof(heap_trace_record_t) * (total_records - index - 1)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // For last element, just zero it out to avoid ambiguity
 | 
					
						
							|  |  |  |         memset(&buffer[index], 0, sizeof(heap_trace_record_t)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     count--; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 20:29:47 +03:00
										 |  |  | #include "heap_trace.inc"
 | 
					
						
							| 
									
										
										
										
											2017-05-11 17:56:17 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-12 20:29:47 +03:00
										 |  |  | #endif /*CONFIG_HEAP_TRACING_STANDALONE*/
 |