1 // Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <string.h>
15 #include <stdint.h>
16 #include <stdbool.h>
17 #include "sdkconfig.h"
18 #include "core_dump_binary.h"
19 #include "esp_core_dump_port.h"
20 #include "esp_core_dump_common.h"
21 
22 #if CONFIG_ESP_COREDUMP_DATA_FORMAT_BIN
23 
24 const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_binary";
25 
26 
esp_core_dump_save_task(core_dump_write_config_t * write_cfg,core_dump_task_header_t * task)27 static esp_err_t esp_core_dump_save_task(core_dump_write_config_t *write_cfg,
28                                          core_dump_task_header_t *task)
29 {
30     esp_err_t err = ESP_FAIL;
31     uint32_t stk_vaddr = 0;
32     uint32_t stk_paddr = 0;
33     uint32_t stk_len = esp_core_dump_get_stack(task, &stk_vaddr, &stk_paddr);
34 
35     stk_len = esp_core_dump_get_memory_len(stk_vaddr, stk_vaddr+stk_len);
36 
37     // Save memory segment header
38     err = write_cfg->write(write_cfg->priv, (void*)task, sizeof(core_dump_task_header_t));
39     if (err != ESP_OK) {
40         ESP_COREDUMP_LOGE("Failed to write task header, error=%d!", err);
41         return err;
42     }
43     // Save TCB block
44     err = write_cfg->write(write_cfg->priv, task->tcb_addr, esp_core_dump_get_tcb_len());
45     if (err != ESP_OK) {
46         ESP_COREDUMP_LOGE("Failed to write TCB, error=%d!", err);
47         return err;
48     }
49     // Save task stack
50     err = write_cfg->write(write_cfg->priv, (void*)stk_paddr, stk_len);
51     if (err != ESP_OK) {
52         ESP_COREDUMP_LOGE("Failed to write stack for task (TCB:%x), stack_start=%x, error=%d!",
53                                 task->tcb_addr,
54                                 stk_vaddr,
55                                 err);
56         return err;
57     }
58 
59     ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x) dump is saved.",
60                                     task->tcb_addr);
61     return ESP_OK;
62 }
63 
esp_core_dump_save_mem_segment(core_dump_write_config_t * write_cfg,core_dump_mem_seg_header_t * seg)64 static esp_err_t esp_core_dump_save_mem_segment(core_dump_write_config_t* write_cfg,
65                                                 core_dump_mem_seg_header_t* seg)
66 {
67     esp_err_t err = ESP_FAIL;
68 
69     if (!esp_core_dump_mem_seg_is_sane(seg->start, seg->size)) {
70         ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu)!",
71                                 seg->start, seg->size);
72         return ESP_FAIL;
73     }
74     // Save TCB address, stack base and stack top addr
75     err = write_cfg->write(write_cfg->priv, (void*)seg, sizeof(core_dump_mem_seg_header_t));
76     if (err != ESP_OK) {
77         ESP_COREDUMP_LOGE("Failed to write memory segment header, error=%d!", err);
78         return err;
79     }
80     // Save memory contents
81     err = write_cfg->write(write_cfg->priv, (void*)seg->start, seg->size);
82     if (err != ESP_OK) {
83         ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu), error=%d!",
84                                 seg->start, seg->size, err);
85         return err;
86     }
87     ESP_COREDUMP_LOG_PROCESS("Memory segment (%x, %lu) is saved.",
88                                 seg->start, seg->size);
89     return ESP_OK;
90 }
91 
esp_core_dump_write_binary(core_dump_write_config_t * write_cfg)92 esp_err_t esp_core_dump_write_binary(core_dump_write_config_t *write_cfg)
93 {
94     esp_err_t err = ESP_OK;
95     uint32_t tcb_sz = esp_core_dump_get_tcb_len();
96     uint32_t data_len = 0;
97     uint32_t bad_tasks_num = 0;
98     core_dump_header_t hdr = { 0 };
99     core_dump_task_header_t task_hdr = { 0 };
100     core_dump_mem_seg_header_t mem_seg = { 0 };
101     void *task = NULL;
102     void *cur_task = NULL;
103 
104     // Verifies all tasks in the snapshot
105     esp_core_dump_reset_tasks_snapshots_iter();
106     while ((task = esp_core_dump_get_next_task(task))) {
107         if (!esp_core_dump_get_task_snapshot(task, &task_hdr, &mem_seg)) {
108             bad_tasks_num++;
109             continue;
110         }
111         hdr.tasks_num++;
112         if (task == esp_core_dump_get_current_task_handle()) {
113             cur_task = task;
114             ESP_COREDUMP_LOG_PROCESS("Task %x %x is first crashed task.", cur_task, task_hdr.tcb_addr);
115         }
116         ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", task_hdr.stack_end-task_hdr.stack_start,
117                                     task_hdr.stack_start, task_hdr.stack_end);
118         // Increase core dump size by task stack size
119         uint32_t stk_vaddr = 0;
120         uint32_t stk_paddr = 0;
121         uint32_t stk_len = esp_core_dump_get_stack(&task_hdr, &stk_vaddr, &stk_paddr);
122         data_len += esp_core_dump_get_memory_len(stk_vaddr, stk_vaddr+stk_len);
123         // Add tcb size
124         data_len += (tcb_sz + sizeof(core_dump_task_header_t));
125         if (mem_seg.size > 0) {
126             ESP_COREDUMP_LOG_PROCESS("Add interrupted task stack %lu bytes @ %x",
127                     mem_seg.size, mem_seg.start);
128             data_len += esp_core_dump_get_memory_len(mem_seg.start, mem_seg.start+mem_seg.size);
129             data_len += sizeof(core_dump_mem_seg_header_t);
130             hdr.mem_segs_num++;
131         }
132     }
133     ESP_COREDUMP_LOGI("Found tasks: good %d, bad %d, mem segs %d", hdr.tasks_num, bad_tasks_num, hdr.mem_segs_num);
134 
135     // Check if current task TCB is broken
136     if (cur_task == NULL) {
137         ESP_COREDUMP_LOG_PROCESS("The current crashed task is broken.");
138         cur_task = esp_core_dump_get_next_task(NULL);
139         if (cur_task == NULL) {
140             ESP_COREDUMP_LOGE("No valid tasks in the system!");
141             return ESP_FAIL;
142         }
143     }
144 
145     // Add user memory regions data size
146     for (coredump_region_t i = COREDUMP_MEMORY_START; i < COREDUMP_MEMORY_MAX; i++) {
147         uint32_t start = 0;
148         int data_sz = esp_core_dump_get_user_ram_info(i, &start);
149         if (data_sz < 0) {
150             ESP_COREDUMP_LOGE("Invalid memory segment size!");
151             return ESP_FAIL;
152         }
153         if (data_sz > 0) {
154             hdr.mem_segs_num++;
155             data_len += sizeof(core_dump_mem_seg_header_t) + esp_core_dump_get_memory_len(start, start + data_sz);
156         }
157     }
158 
159     // Add core dump header size
160     data_len += sizeof(core_dump_header_t);
161 
162     ESP_COREDUMP_LOG_PROCESS("Core dump length=%lu, tasks processed: %d, broken tasks: %d",
163                                 data_len, hdr.tasks_num, bad_tasks_num);
164     // Prepare write
165     if (write_cfg->prepare) {
166         err = write_cfg->prepare(write_cfg->priv, &data_len);
167         if (err != ESP_OK) {
168             ESP_COREDUMP_LOGE("Failed to prepare core dump, error=%d!", err);
169             return err;
170         }
171     }
172 
173     // Write start
174     if (write_cfg->start) {
175         err = write_cfg->start(write_cfg->priv);
176         if (err != ESP_OK) {
177             ESP_COREDUMP_LOGE("Failed to start core dump, error=%d!", err);
178             return err;
179         }
180     }
181 
182     // Write header
183     hdr.data_len  = data_len;
184     hdr.version   = COREDUMP_VERSION_BIN_CURRENT;
185     hdr.tcb_sz    = tcb_sz;
186     err = write_cfg->write(write_cfg->priv, &hdr, sizeof(core_dump_header_t));
187     if (err != ESP_OK) {
188         ESP_COREDUMP_LOGE("Failed to write core dump header error=%d!", err);
189         return err;
190     }
191 
192     // Save tasks
193     esp_core_dump_reset_tasks_snapshots_iter();
194     // Write first crashed task data first (not always first task in the snapshot)
195     ESP_COREDUMP_LOGD("Save first crashed task %x", cur_task);
196     if (esp_core_dump_get_task_snapshot(cur_task, &task_hdr, NULL)) {
197         err = esp_core_dump_save_task(write_cfg, &task_hdr);
198         if (err != ESP_OK) {
199             ESP_COREDUMP_LOGE("Failed to save first crashed task %x, error=%d!",
200                                 task_hdr.tcb_addr, err);
201             return err;
202         }
203     }
204     // Write all other tasks in the snapshot
205     task = NULL;
206     while ((task = esp_core_dump_get_next_task(task))) {
207         if (!esp_core_dump_get_task_snapshot(task, &task_hdr, NULL))
208             continue;
209         // Skip first crashed task
210         if (task == cur_task) {
211             continue;
212         }
213         ESP_COREDUMP_LOGD("Save task %x (TCB:%x, stack:%x..%x)", task, task_hdr.tcb_addr, task_hdr.stack_start, task_hdr.stack_end);
214         err = esp_core_dump_save_task(write_cfg, &task_hdr);
215         if (err != ESP_OK) {
216             ESP_COREDUMP_LOGE("Failed to save core dump task %x, error=%d!",
217                                     task_hdr.tcb_addr, err);
218             return err;
219         }
220     }
221 
222     // Save interrupted stacks of the tasks
223     // Actually there can be tasks interrupted at the same time, one on every core including the crashed one.
224     task = NULL;
225     esp_core_dump_reset_tasks_snapshots_iter();
226     while ((task = esp_core_dump_get_next_task(task))) {
227         if (!esp_core_dump_get_task_snapshot(task, &task_hdr, &mem_seg))
228             continue;
229         if (mem_seg.size > 0) {
230             ESP_COREDUMP_LOG_PROCESS("Save interrupted task stack %lu bytes @ %x",
231                     mem_seg.size, mem_seg.start);
232             err = esp_core_dump_save_mem_segment(write_cfg, &mem_seg);
233             if (err != ESP_OK) {
234                 ESP_COREDUMP_LOGE("Failed to save interrupted task stack, error=%d!", err);
235                 return err;
236             }
237         }
238     }
239 
240     // save user memory regions
241     if (esp_core_dump_get_user_ram_segments() > 0) {
242         for (coredump_region_t i = COREDUMP_MEMORY_START; i < COREDUMP_MEMORY_MAX; i++) {
243             uint32_t start = 0;
244             int data_sz = esp_core_dump_get_user_ram_info(i, &start);
245 
246             if (data_sz < 0) {
247                 ESP_COREDUMP_LOGE("Invalid memory segment size");
248                 return ESP_FAIL;
249             }
250 
251             if (data_sz > 0) {
252                 mem_seg.start = start;
253                 mem_seg.size = esp_core_dump_get_memory_len(start, start + data_sz);;
254                 ESP_COREDUMP_LOG_PROCESS("Save user memory region %lu bytes @ %x",
255                         mem_seg.size, mem_seg.start);
256                 err = esp_core_dump_save_mem_segment(write_cfg, &mem_seg);
257                 if (err != ESP_OK) {
258                     ESP_COREDUMP_LOGE("Failed to save user memory region, error=%d!", err);
259                     return err;
260                 }
261             }
262         }
263     }
264 
265     // Write end
266     if (write_cfg->end) {
267         err = write_cfg->end(write_cfg->priv);
268         if (err != ESP_OK) {
269             ESP_COREDUMP_LOGE("Failed to end core dump error=%d!", err);
270             return err;
271         }
272     }
273     if (bad_tasks_num) {
274         ESP_COREDUMP_LOGE("Found %d broken tasks!", bad_tasks_num);
275     }
276     return err;
277 }
278 
279 #endif
280