Skip to content

Commit 165da46

Browse files
[ESP32] Read core dump from flash, for diagnostic crash logs (#32192)
* [ESP32] Read core dump from flash, for diagnostic crash logs * Restyled by clang-format * Restyled by prettier-markdown * add espcoredump to .wordlist * Add some null checks and init the struct members --------- Co-authored-by: Restyled.io <commits@restyled.io>
1 parent 70e08ec commit 165da46

File tree

8 files changed

+286
-78
lines changed

8 files changed

+286
-78
lines changed

.github/.wordlist.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ epochStartTime
523523
eq
524524
errorValue
525525
esd
526+
espcoredump
526527
ESPPORT
527528
Espressif
528529
esptool
@@ -1621,4 +1622,4 @@ zephyrproject
16211622
zhengyaohan
16221623
Zigbee
16231624
zigbeealliance
1624-
zigbeethread
1625+
zigbeethread

examples/temperature-measurement-app/esp32/README.md

+31-1
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,39 @@ chip-tool pairing ble-wifi 1 SSID PASSPHRASE 20202021 3840
5151
chip-tool diagnosticlogs retrieve-logs-request 0 0 1 0
5252
5353
# Read network diagnostic using BDX protocol
54-
chip-tool diagnosticlogs retrieve-logs-request 1 0 1 0 --TransferFileDesignator network-diag.log
54+
chip-tool interactive start
55+
> diagnosticlogs retrieve-logs-request 1 1 1 0 --TransferFileDesignator network-diag.log
56+
# Retrieve crash over BDX
57+
> diagnosticlogs retrieve-logs-request 1 1 1 0 --TransferFileDesignator crash.bin
5558
```
5659

60+
esp-idf supports storing and retrieving
61+
[core dump in flash](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/core_dump.html#core-dump-to-flash).
62+
63+
To support that, application needs to add core dump partition's entry in
64+
[partitons.csv](partitions.csv#7) and we need to enable few menuconfig options.
65+
66+
```
67+
CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=y
68+
CONFIG_ESP32_COREDUMP_DATA_FORMAT_ELF=y
69+
```
70+
71+
This example's partition table and sdkconfig.default are already modified
72+
73+
- Retrieve the core dump using diagnostic logs cluster
74+
75+
```
76+
# Read crash logs over BDX
77+
chip-tool interactive start
78+
> diagnosticlogs retrieve-logs-request 1 1 1 0 --TransferFileDesignator crash.bin
79+
```
80+
81+
- Decode the crash logs, using espcoredump.py
82+
```
83+
espcoredump.py --chip (CHIP) info_corefile --core /tmp/crash.bin \
84+
--core-format elf build/chip-temperature-measurement-app.elf
85+
```
86+
5787
## Optimization
5888
5989
Optimization related to WiFi, BLuetooth, Asserts etc are the part of this

examples/temperature-measurement-app/esp32/main/CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ set(SRC_DIRS_LIST
5555
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/providers"
5656
)
5757

58-
set(PRIV_REQUIRES_LIST chip QRCode bt nvs_flash)
58+
set(PRIV_REQUIRES_LIST chip QRCode bt nvs_flash bootloader_support espcoredump)
5959

6060
if (CONFIG_ENABLE_PW_RPC)
6161
# Append additional directories for RPC build
@@ -83,7 +83,7 @@ endif (CONFIG_ENABLE_PW_RPC)
8383
idf_component_register(PRIV_INCLUDE_DIRS ${PRIV_INCLUDE_DIRS_LIST}
8484
SRC_DIRS ${SRC_DIRS_LIST}
8585
PRIV_REQUIRES ${PRIV_REQUIRES_LIST}
86-
EMBED_FILES diagnostic_logs/end_user_support.log diagnostic_logs/network_diag.log diagnostic_logs/crash.log)
86+
EMBED_FILES diagnostic_logs/end_user_support.log diagnostic_logs/network_diag.log)
8787

8888
include("${CHIP_ROOT}/build/chip/esp32/esp32_codegen.cmake")
8989
chip_app_component_codegen("${CHIP_ROOT}/examples/temperature-measurement-app/temperature-measurement-common/temperature-measurement.matter")

examples/temperature-measurement-app/esp32/main/diagnostic-logs-provider-delegate-impl.cpp

+194-48
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,41 @@
1919
#include <diagnostic-logs-provider-delegate-impl.h>
2020
#include <lib/support/SafeInt.h>
2121

22+
#if defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
23+
#include <esp_core_dump.h>
24+
#include <esp_flash_encrypt.h>
25+
// Its a bit hackish but we need this in order to pull in the sizeof(core_dump_header_t)
26+
// we can even use the static 20 but, what if that gets chagned?
27+
#include "../include_core_dump/esp_core_dump_types.h"
28+
#endif // defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
29+
2230
using namespace chip;
2331
using namespace chip::app::Clusters::DiagnosticLogs;
2432

2533
LogProvider LogProvider::sInstance;
34+
LogProvider::CrashLogContext LogProvider::sCrashLogContext;
2635

2736
namespace {
2837
bool IsValidIntent(IntentEnum intent)
2938
{
3039
return intent != IntentEnum::kUnknownEnumValue;
3140
}
3241

33-
// end_user_support.log, network_diag.log, and crash.log files are embedded in the firmware
42+
// end_user_support.log and network_diag.log files are embedded in the firmware
3443
extern const uint8_t endUserSupportLogStart[] asm("_binary_end_user_support_log_start");
3544
extern const uint8_t endUserSupportLogEnd[] asm("_binary_end_user_support_log_end");
3645

3746
extern const uint8_t networkDiagnosticLogStart[] asm("_binary_network_diag_log_start");
3847
extern const uint8_t networkDiagnosticLogEnd[] asm("_binary_network_diag_log_end");
39-
40-
extern const uint8_t crashLogStart[] asm("_binary_crash_log_start");
41-
extern const uint8_t crashLogEnd[] asm("_binary_crash_log_end");
4248
} // namespace
4349

4450
LogProvider::~LogProvider()
4551
{
46-
for (auto sessionSpan : mSessionSpanMap)
52+
for (auto sessionSpan : mSessionContextMap)
4753
{
4854
Platform::MemoryFree(sessionSpan.second);
4955
}
50-
mSessionSpanMap.clear();
56+
mSessionContextMap.clear();
5157
}
5258

5359
CHIP_ERROR LogProvider::GetLogForIntent(IntentEnum intent, MutableByteSpan & outBuffer, Optional<uint64_t> & outTimeStamp,
@@ -69,93 +75,233 @@ CHIP_ERROR LogProvider::GetLogForIntent(IntentEnum intent, MutableByteSpan & out
6975
return CHIP_NO_ERROR;
7076
}
7177

72-
const uint8_t * LogProvider::GetDataStartForIntent(IntentEnum intent)
78+
size_t LogProvider::GetSizeForIntent(IntentEnum intent)
7379
{
7480
switch (intent)
7581
{
7682
case IntentEnum::kEndUserSupport:
77-
return &endUserSupportLogStart[0];
83+
return static_cast<size_t>(endUserSupportLogEnd - endUserSupportLogStart);
7884
case IntentEnum::kNetworkDiag:
79-
return &networkDiagnosticLogStart[0];
85+
return static_cast<size_t>(networkDiagnosticLogEnd - networkDiagnosticLogStart);
8086
case IntentEnum::kCrashLogs:
81-
return &crashLogStart[0];
87+
return GetCrashSize();
8288
default:
83-
return nullptr;
89+
return 0;
8490
}
8591
}
8692

87-
size_t LogProvider::GetSizeForIntent(IntentEnum intent)
93+
size_t LogProvider::GetCrashSize()
8894
{
95+
size_t outSize = 0;
96+
97+
#if defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
98+
size_t unusedOutAddr;
99+
esp_err_t esp_err = esp_core_dump_image_get(&unusedOutAddr, &outSize);
100+
VerifyOrReturnValue(esp_err == ESP_OK, 0, ChipLogError(DeviceLayer, "Failed to get core dump image, esp_err:%d", esp_err));
101+
#endif // defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
102+
103+
return outSize;
104+
}
105+
106+
CHIP_ERROR LogProvider::MapCrashPartition(CrashLogContext * context)
107+
{
108+
#if defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
109+
size_t outAddr, outSize;
110+
esp_err_t esp_err = esp_core_dump_image_get(&outAddr, &outSize);
111+
VerifyOrReturnError(esp_err == ESP_OK, CHIP_ERROR(ChipError::Range::kPlatform, esp_err),
112+
ChipLogError(DeviceLayer, "Failed to get core dump image, esp_err:%d", esp_err));
113+
114+
/* map the full core dump parition, including the checksum. */
115+
esp_err = spi_flash_mmap(outAddr, outSize, SPI_FLASH_MMAP_DATA, &context->mappedAddress, &context->mappedHandle);
116+
VerifyOrReturnError(esp_err == ESP_OK, CHIP_ERROR(ChipError::Range::kPlatform, esp_err),
117+
ChipLogError(DeviceLayer, "Failed to mmap the crash partition, esp_err:%d", esp_err));
118+
119+
context->crashSize = static_cast<uint32_t>(outSize);
120+
return CHIP_NO_ERROR;
121+
#else
122+
return CHIP_ERROR_NOT_FOUND;
123+
#endif // defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
124+
}
125+
126+
CHIP_ERROR LogProvider::PrepareLogContextForIntent(LogContext * context, IntentEnum intent)
127+
{
128+
context->intent = intent;
129+
89130
switch (intent)
90131
{
132+
case IntentEnum::kEndUserSupport: {
133+
context->EndUserSupport.span =
134+
ByteSpan(&endUserSupportLogStart[0], static_cast<size_t>(endUserSupportLogEnd - endUserSupportLogStart));
135+
}
136+
break;
137+
138+
case IntentEnum::kNetworkDiag: {
139+
context->NetworkDiag.span =
140+
ByteSpan(&networkDiagnosticLogStart[0], static_cast<size_t>(networkDiagnosticLogEnd - networkDiagnosticLogStart));
141+
}
142+
break;
143+
144+
case IntentEnum::kCrashLogs: {
145+
#if defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
146+
sCrashLogContext.Reset();
147+
context->Crash.logContext = &sCrashLogContext;
148+
149+
CHIP_ERROR err = MapCrashPartition(context->Crash.logContext);
150+
VerifyOrReturnError(err == CHIP_NO_ERROR, err, context->Crash.logContext = nullptr);
151+
152+
context->Crash.logContext->readOffset = sizeof(core_dump_header_t);
153+
context->Crash.logContext->isMapped = true;
154+
#else
155+
return CHIP_ERROR_NOT_FOUND;
156+
#endif // defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
157+
}
158+
break;
159+
160+
default:
161+
return CHIP_ERROR_INVALID_ARGUMENT;
162+
}
163+
164+
return CHIP_NO_ERROR;
165+
}
166+
167+
void LogProvider::CleanupLogContextForIntent(LogContext * context)
168+
{
169+
switch (context->intent)
170+
{
91171
case IntentEnum::kEndUserSupport:
92-
return static_cast<size_t>(endUserSupportLogEnd - endUserSupportLogStart);
172+
break;
173+
93174
case IntentEnum::kNetworkDiag:
94-
return static_cast<size_t>(networkDiagnosticLogEnd - networkDiagnosticLogStart);
95-
case IntentEnum::kCrashLogs:
96-
return static_cast<size_t>(crashLogEnd - crashLogStart);
175+
break;
176+
177+
case IntentEnum::kCrashLogs: {
178+
#if defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
179+
CrashLogContext * logContext = context->Crash.logContext;
180+
spi_flash_munmap(logContext->mappedHandle);
181+
logContext->Reset();
182+
#endif // defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
183+
}
184+
break;
185+
97186
default:
98-
return 0;
187+
break;
99188
}
100189
}
101190

191+
CHIP_ERROR LogProvider::GetDataForIntent(LogContext * context, MutableByteSpan & outBuffer, bool & outIsEndOfLog)
192+
{
193+
switch (context->intent)
194+
{
195+
case IntentEnum::kEndUserSupport: {
196+
auto dataSize = context->EndUserSupport.span.size();
197+
auto count = std::min(dataSize, outBuffer.size());
198+
199+
VerifyOrReturnError(CanCastTo<off_t>(count), CHIP_ERROR_INVALID_ARGUMENT, outBuffer.reduce_size(0));
200+
ReturnErrorOnFailure(CopySpanToMutableSpan(ByteSpan(context->EndUserSupport.span.data(), count), outBuffer));
201+
202+
outIsEndOfLog = dataSize == count;
203+
if (!outIsEndOfLog)
204+
{
205+
// reduce the span after reading count bytes
206+
context->EndUserSupport.span = context->EndUserSupport.span.SubSpan(count);
207+
}
208+
}
209+
break;
210+
211+
case IntentEnum::kNetworkDiag: {
212+
auto dataSize = context->NetworkDiag.span.size();
213+
auto count = std::min(dataSize, outBuffer.size());
214+
215+
VerifyOrReturnError(CanCastTo<off_t>(count), CHIP_ERROR_INVALID_ARGUMENT, outBuffer.reduce_size(0));
216+
ReturnErrorOnFailure(CopySpanToMutableSpan(ByteSpan(context->NetworkDiag.span.data(), count), outBuffer));
217+
218+
outIsEndOfLog = dataSize == count;
219+
if (!outIsEndOfLog)
220+
{
221+
// reduce the span after reading count bytes
222+
context->NetworkDiag.span = context->NetworkDiag.span.SubSpan(count);
223+
}
224+
}
225+
break;
226+
227+
case IntentEnum::kCrashLogs: {
228+
#if defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
229+
CrashLogContext * logContext = context->Crash.logContext;
230+
size_t dataSize = logContext->crashSize - logContext->readOffset;
231+
auto count = std::min(dataSize, outBuffer.size());
232+
233+
VerifyOrReturnError(CanCastTo<off_t>(count), CHIP_ERROR_INVALID_ARGUMENT, outBuffer.reduce_size(0));
234+
235+
const uint8_t * readAddr = reinterpret_cast<const uint8_t *>(logContext->mappedAddress) + logContext->readOffset;
236+
memcpy(outBuffer.data(), readAddr, count);
237+
outBuffer.reduce_size(count);
238+
239+
logContext->readOffset += count;
240+
outIsEndOfLog = dataSize == count;
241+
#else
242+
outBuffer.reduce_size(0);
243+
return CHIP_ERROR_NOT_FOUND;
244+
#endif // defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF)
245+
}
246+
break;
247+
248+
default:
249+
return CHIP_ERROR_INVALID_ARGUMENT;
250+
}
251+
252+
return CHIP_NO_ERROR;
253+
}
254+
102255
CHIP_ERROR LogProvider::StartLogCollection(IntentEnum intent, LogSessionHandle & outHandle, Optional<uint64_t> & outTimeStamp,
103256
Optional<uint64_t> & outTimeSinceBoot)
104257
{
105258
VerifyOrReturnValue(IsValidIntent(intent), CHIP_ERROR_INVALID_ARGUMENT);
106259

107-
const uint8_t * dataStart = GetDataStartForIntent(intent);
108-
VerifyOrReturnError(dataStart, CHIP_ERROR_NOT_FOUND);
109-
110-
size_t dataSize = GetSizeForIntent(intent);
111-
VerifyOrReturnError(dataSize, CHIP_ERROR_NOT_FOUND);
260+
// In case of crash logs we can only mmap at max once, so check before doing anything
261+
if (intent == IntentEnum::kCrashLogs)
262+
{
263+
VerifyOrReturnError(sCrashLogContext.isMapped == false, CHIP_ERROR_INCORRECT_STATE,
264+
ChipLogError(DeviceLayer, "Crash partition already mapped"));
265+
}
112266

113-
ByteSpan * span = reinterpret_cast<ByteSpan *>(Platform::MemoryCalloc(1, sizeof(ByteSpan)));
114-
VerifyOrReturnValue(span, CHIP_ERROR_NO_MEMORY);
267+
LogContext * context = reinterpret_cast<LogContext *>(Platform::MemoryCalloc(1, sizeof(LogContext)));
268+
VerifyOrReturnValue(context != nullptr, CHIP_ERROR_NO_MEMORY);
115269

116-
*span = ByteSpan(dataStart, dataSize);
270+
CHIP_ERROR err = PrepareLogContextForIntent(context, intent);
271+
VerifyOrReturnError(err == CHIP_NO_ERROR, err, Platform::MemoryFree(context));
117272

118273
mLogSessionHandle++;
119274
// If the session handle rolls over to UINT16_MAX which is invalid, reset to 0.
120275
VerifyOrDo(mLogSessionHandle != kInvalidLogSessionHandle, mLogSessionHandle = 0);
121276

122-
outHandle = mLogSessionHandle;
123-
mSessionSpanMap[mLogSessionHandle] = span;
277+
outHandle = mLogSessionHandle;
278+
mSessionContextMap[mLogSessionHandle] = context;
279+
124280
return CHIP_NO_ERROR;
125281
}
126282

127283
CHIP_ERROR LogProvider::EndLogCollection(LogSessionHandle sessionHandle)
128284
{
129285
VerifyOrReturnValue(sessionHandle != kInvalidLogSessionHandle, CHIP_ERROR_INVALID_ARGUMENT);
130-
VerifyOrReturnValue(mSessionSpanMap.count(sessionHandle), CHIP_ERROR_INVALID_ARGUMENT);
286+
VerifyOrReturnValue(mSessionContextMap.count(sessionHandle), CHIP_ERROR_INVALID_ARGUMENT);
131287

132-
ByteSpan * span = mSessionSpanMap[sessionHandle];
133-
mSessionSpanMap.erase(sessionHandle);
288+
LogContext * context = mSessionContextMap[sessionHandle];
289+
VerifyOrReturnError(context, CHIP_ERROR_INCORRECT_STATE);
290+
291+
CleanupLogContextForIntent(context);
292+
Platform::MemoryFree(context);
293+
mSessionContextMap.erase(sessionHandle);
134294

135-
Platform::MemoryFree(span);
136295
return CHIP_NO_ERROR;
137296
}
138297

139298
CHIP_ERROR LogProvider::CollectLog(LogSessionHandle sessionHandle, MutableByteSpan & outBuffer, bool & outIsEndOfLog)
140299
{
141300
VerifyOrReturnValue(sessionHandle != kInvalidLogSessionHandle, CHIP_ERROR_INVALID_ARGUMENT);
142-
VerifyOrReturnValue(mSessionSpanMap.count(sessionHandle), CHIP_ERROR_INVALID_ARGUMENT);
143-
144-
ByteSpan * span = mSessionSpanMap[sessionHandle];
145-
auto dataSize = span->size();
146-
auto count = std::min(dataSize, outBuffer.size());
147-
148-
VerifyOrReturnError(CanCastTo<off_t>(count), CHIP_ERROR_INVALID_ARGUMENT, outBuffer.reduce_size(0));
149-
150-
ReturnErrorOnFailure(CopySpanToMutableSpan(ByteSpan(span->data(), count), outBuffer));
301+
VerifyOrReturnValue(mSessionContextMap.count(sessionHandle), CHIP_ERROR_INVALID_ARGUMENT);
151302

152-
outIsEndOfLog = dataSize == count;
303+
LogContext * context = mSessionContextMap[sessionHandle];
304+
VerifyOrReturnError(context, CHIP_ERROR_INCORRECT_STATE);
153305

154-
if (!outIsEndOfLog)
155-
{
156-
// reduce the span after reading count bytes
157-
*span = span->SubSpan(count);
158-
}
159-
160-
return CHIP_NO_ERROR;
306+
return GetDataForIntent(context, outBuffer, outIsEndOfLog);
161307
}

0 commit comments

Comments
 (0)