* Copyright (c) 2026 Huawei Device Co., 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 "cppcrash_formatter.h"
#ifndef is_ohos_lite
#include "cJSON.h"
#include "dfx_util.h"
#include "dfx_define.h"
#include "dfx_log.h"
#include "string_printf.h"
#include "string_util.h"
#include "dfx_map.h"
#endif
namespace OHOS {
namespace HiviewDFX {
#ifndef is_ohos_lite
bool CppCrashJsonFormatter::NeedFormatCrashInfo()
{
return CppCrashInfoCollector::Instance().GetNeedFormatFlag();
}
std::string CppCrashJsonFormatter::FormatCrashInfo()
{
if (!NeedFormatCrashInfo()) {
return "";
}
cJSON* root = cJSON_CreateObject();
if (root == nullptr) {
DFXLOGE("Failed to create cJSON object for root");
return "";
}
CppCrashInfoCollector& collector = CppCrashInfoCollector::Instance();
cJSON* versionItem = cJSON_CreateString(CPPCRASH_JSON_VERSION_VALUE);
if (versionItem == nullptr) {
DFXLOGE("Failed to create cJSON string for version item");
cJSON_Delete(root);
return "";
}
cJSON_AddItemToObject(root, CPPCRASH_JSON_VERSION_KEY, versionItem);
AddHeadInfo(root, collector);
AddSignalInfo(root, collector);
AddThreadInfo(root, collector);
AddSubmitterStacktrace(root, collector);
AddRegistersInfo(root, collector);
AddExtraCrashInfo(root, collector);
AddMemoryNearRegister(root, collector);
AddFaultStack(root, collector);
AddMapsInfo(root, collector);
AddOpenFiles(root, collector);
char* str = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
if (str == nullptr) {
DFXLOGE("Failed to print cJSON to string");
return "";
}
std::string result(str);
free(str);
collector.SetCrashInfoSize(result.size());
return result;
}
void CppCrashJsonFormatter::AddHeadInfo(cJSON* root, CppCrashInfoCollector& collector)
{
if (root == nullptr) {
DFXLOGE("AddHeadInfo: root is nullptr");
return;
}
AddStringValue(root, "BUILD_INFO", collector.GetBuildInfo());
AddStringValue(root, "TIMESTAMP", collector.GetTimestamp());
cJSON* pidItem = cJSON_CreateNumber(collector.GetPid());
cJSON* uidItem = cJSON_CreateNumber(collector.GetUid());
if (pidItem != nullptr) {
cJSON_AddItemToObject(root, "PID", pidItem);
}
if (uidItem != nullptr) {
cJSON_AddItemToObject(root, "UID", uidItem);
}
AddStringValue(root, "HITRACEID", collector.GetHiTraceId());
AddStringValue(root, "PNAME", collector.GetPname(), true);
AddStringValue(root, "APP_RUNNING_UNIQUE_ID", collector.GetAppRunningUniqueId());
AddStringValue(root, "PROCESS_LIFETIME", collector.GetProcessLifeTime());
AddStringValue(root, "PROCESS_RSS_MEMINFO", collector.GetProcessRssMeminfo());
AddStringValue(root, "LOG_SOURCE", collector.GetLogSource());
AddStringValue(root, "REASON", collector.GetReason());
AddStringValue(root, "LAST_FATAL_MESSAGE", collector.GetLastFatalMessage());
cJSON* config = cJSON_CreateObject();
if (config == nullptr) {
DFXLOGE("Failed to create cJSON object for config");
return;
}
AddBoolValue(config, "EXTEND_PC_LR_PRINTING", collector.GetExtendPcLrPrinting());
AddStringValue(config, "LOG_CUT_OFF_SIZE", collector.GetLogCutOffSizeStr());
AddBoolValue(config, "SIMPLIFY_MAPS_PRINTING", collector.GetSimplifyVmaPrinting());
AddBoolValue(config, "MERGE_APP_LOG_PRINTING", collector.GetMergeAppLog());
AddBoolValue(config, "ENABLE_MINIDUMP_LOG", collector.GetMinidumpLog());
int size = cJSON_GetArraySize(config);
if (size > 0) {
cJSON_AddItemToObject(root, "ENABLED_APP_LOG_CONFIG", config);
} else {
cJSON_Delete(config);
}
}
void CppCrashJsonFormatter::AddSignalInfo(cJSON* root, CppCrashInfoCollector& collector)
{
if (root == nullptr) {
DFXLOGE("AddSignalInfo: root is nullptr");
return;
}
int32_t signo = collector.GetSignalSigno();
cJSON* signal = cJSON_CreateObject();
if (signal == nullptr) {
DFXLOGE("Failed to create cJSON object for signal");
return;
}
cJSON* signoItem = cJSON_CreateNumber(signo);
cJSON* codeItem = cJSON_CreateNumber(collector.GetSignalCode());
cJSON* addrItem = cJSON_CreateString(collector.GetSignalAddress().c_str());
if (signoItem != nullptr) {
cJSON_AddItemToObject(signal, "signo", signoItem);
}
if (codeItem != nullptr) {
cJSON_AddItemToObject(signal, "code", codeItem);
}
if (addrItem != nullptr) {
cJSON_AddItemToObject(signal, "address", addrItem);
}
cJSON_AddItemToObject(root, "SIGNAL", signal);
}
void CppCrashJsonFormatter::AddThreadInfo(cJSON* root, CppCrashInfoCollector& collector)
{
if (root == nullptr) {
DFXLOGE("AddThreadInfo: root is nullptr");
return;
}
const ThreadInfo& keyThread = collector.GetKeyThread();
if (!keyThread.frames.empty() || !keyThread.threadName.empty() || keyThread.tid != 0) {
cJSON* keyThreadJson = FillThreadJson(keyThread);
if (keyThreadJson != nullptr) {
cJSON_AddItemToObject(root, "KEY_THREAD_INFO", keyThreadJson);
}
}
const std::vector<ThreadInfo>& otherThreads = collector.GetOtherThreads();
if (otherThreads.empty()) {
return;
}
cJSON* threadsArray = cJSON_CreateArray();
if (threadsArray == nullptr) {
DFXLOGE("Failed to create cJSON array for threads");
return;
}
for (const auto& thread : otherThreads) {
cJSON* threadJson = FillThreadJson(thread);
if (threadJson != nullptr) {
cJSON_AddItemToArray(threadsArray, threadJson);
}
}
cJSON_AddItemToObject(root, "OTHER_THREAD_INFO", threadsArray);
}
void CppCrashJsonFormatter::AddSubmitterStacktrace(cJSON* root, CppCrashInfoCollector& collector)
{
AddStringValue(root, "SUBMITTER_STACKTRACE", collector.GetSubmitterStacktrace());
}
void CppCrashJsonFormatter::AddRegistersInfo(cJSON* root, CppCrashInfoCollector& collector)
{
AddStringValue(root, "KEY_THREAD_REGISTERS", collector.GetRegisters());
}
void CppCrashJsonFormatter::AddExtraCrashInfo(cJSON* root, CppCrashInfoCollector& collector)
{
AddStringValue(root, "EXTRA_CRASH_INFO", collector.GetExtraCrashInfo());
}
void CppCrashJsonFormatter::AddMemoryNearRegister(cJSON* root, CppCrashInfoCollector& collector)
{
AddStringValue(root, "MEMORY_NEAR_REGISTERS", collector.GetMemoryNearRegister());
}
void CppCrashJsonFormatter::AddFaultStack(cJSON* root, CppCrashInfoCollector& collector)
{
AddStringValue(root, "FAULT_STACK", collector.GetFaultStack());
}
void CppCrashJsonFormatter::AddMapsInfo(cJSON* root, CppCrashInfoCollector& collector)
{
AddStringValue(root, "PROCESS_MAPS", collector.GetMaps());
}
void CppCrashJsonFormatter::AddOpenFiles(cJSON* root, CppCrashInfoCollector& collector)
{
AddStringValue(root, "OPEN_FILES", collector.GetOpenFiles());
}
bool CppCrashJsonFormatter::AddStringValue(cJSON* root, const char* key, const std::string& value, bool isRequire)
{
if (root == nullptr || key == nullptr) {
return false;
}
if (value.empty() && !isRequire) {
return false;
}
cJSON* item = cJSON_CreateString(value.c_str());
if (item == nullptr) {
DFXLOGE("Failed to create cJSON string for key:%{public}s", key);
return false;
}
cJSON_AddItemToObject(root, key, item);
return true;
}
bool CppCrashJsonFormatter::AddBoolValue(cJSON* root, const char* key, bool value)
{
if (root == nullptr || key == nullptr || !value) {
return false;
}
cJSON* item = cJSON_CreateBool(value);
if (item == nullptr) {
DFXLOGE("Failed to create cJSON bool for key:%{public}s", key);
return false;
}
cJSON_AddItemToObject(root, key, item);
return true;
}
cJSON* CppCrashJsonFormatter::FillThreadJson(const ThreadInfo& threadInfo)
{
cJSON* threadJson = cJSON_CreateObject();
if (threadJson == nullptr) {
DFXLOGE("Failed to create cJSON object for thread");
return nullptr;
}
cJSON* nameItem = cJSON_CreateString(threadInfo.threadName.c_str());
cJSON* tidItem = cJSON_CreateNumber(threadInfo.tid);
if (nameItem != nullptr) {
cJSON_AddItemToObject(threadJson, "thread_name", nameItem);
}
if (tidItem != nullptr) {
cJSON_AddItemToObject(threadJson, "tid", tidItem);
}
cJSON* framesJson = FillFramesJson(threadInfo.frames);
if (framesJson != nullptr) {
cJSON_AddItemToObject(threadJson, "frames", framesJson);
}
return threadJson;
}
cJSON* CppCrashJsonFormatter::FillFramesJson(const std::vector<DfxFrame>& frames)
{
cJSON* framesArray = cJSON_CreateArray();
if (framesArray == nullptr) {
DFXLOGE("Failed to create cJSON array for frames");
return nullptr;
}
for (const auto& frame : frames) {
cJSON* frameJson = frame.isJsFrame ? FillJsFrameJson(frame) : FillNativeFrameJson(frame);
if (frameJson != nullptr) {
cJSON_AddItemToArray(framesArray, frameJson);
}
#if defined(__aarch64__)
if (!frame.isJsFrame && IsLastValidFrame(frame)) {
break;
}
#endif
}
return framesArray;
}
cJSON* CppCrashJsonFormatter::FillNativeFrameJson(const DfxFrame& frame)
{
cJSON* frameJson = cJSON_CreateObject();
if (frameJson == nullptr) {
DFXLOGE("Failed to create cJSON object for native frame");
return nullptr;
}
std::string pcStr = FormatPc(frame.relPc);
cJSON* pcItem = cJSON_CreateString(pcStr.c_str());
std::string funcName;
if (frame.funcName.length() <= MAX_FUNC_NAME_LEN) {
funcName = frame.funcName;
}
cJSON* symItem = cJSON_CreateString(funcName.c_str());
cJSON* offsetItem = cJSON_CreateNumber(frame.funcOffset);
std::string strippedMapName = DfxMap::UnFormatMapName(frame.mapName);
cJSON* fileItem = cJSON_CreateString(strippedMapName.c_str());
cJSON* buildIdItem = cJSON_CreateString(frame.buildId.c_str());
if (pcItem != nullptr) {
cJSON_AddItemToObject(frameJson, "pc", pcItem);
}
if (symItem != nullptr) {
cJSON_AddItemToObject(frameJson, "symbol", symItem);
}
if (offsetItem != nullptr) {
cJSON_AddItemToObject(frameJson, "offset", offsetItem);
}
if (fileItem != nullptr) {
cJSON_AddItemToObject(frameJson, "file", fileItem);
}
if (buildIdItem != nullptr) {
cJSON_AddItemToObject(frameJson, "buildId", buildIdItem);
}
return frameJson;
}
cJSON* CppCrashJsonFormatter::FillJsFrameJson(const DfxFrame& frame)
{
cJSON* frameJson = cJSON_CreateObject();
if (frameJson == nullptr) {
DFXLOGE("Failed to create cJSON object for js frame");
return nullptr;
}
cJSON* fileItem = cJSON_CreateString(frame.mapName.c_str());
cJSON* pkgItem = cJSON_CreateString(frame.packageName.c_str());
cJSON* symItem = cJSON_CreateString(frame.funcName.c_str());
cJSON* lineItem = cJSON_CreateNumber(frame.line);
cJSON* colItem = cJSON_CreateNumber(frame.column);
if (fileItem != nullptr) {
cJSON_AddItemToObject(frameJson, "file", fileItem);
}
if (pkgItem != nullptr) {
cJSON_AddItemToObject(frameJson, "packageName", pkgItem);
}
if (symItem != nullptr) {
cJSON_AddItemToObject(frameJson, "symbol", symItem);
}
if (lineItem != nullptr) {
cJSON_AddItemToObject(frameJson, "line", lineItem);
}
if (colItem != nullptr) {
cJSON_AddItemToObject(frameJson, "column", colItem);
}
return frameJson;
}
std::string CppCrashJsonFormatter::FormatPc(uint64_t relPc)
{
#ifdef __LP64__
return StringPrintf("%016lx", relPc);
#else
return StringPrintf("%08llx", relPc);
#endif
}
bool CppCrashJsonFormatter::IsLastValidFrame(const DfxFrame& frame)
{
static uintptr_t libcStartPc = 0;
static uintptr_t libffrtStartEntry = 0;
if (((libcStartPc != 0) && (frame.pc == libcStartPc)) ||
((libffrtStartEntry != 0) && (frame.pc == libffrtStartEntry))) {
return true;
}
if (frame.mapName.find("ld-musl-aarch64.so.1") != std::string::npos &&
frame.funcName.find("start") != std::string::npos) {
libcStartPc = frame.pc;
return true;
}
if (frame.mapName.find("libffrt") != std::string::npos &&
frame.funcName.find("CoStartEntry") != std::string::npos) {
libffrtStartEntry = frame.pc;
return true;
}
return false;
}
#else
std::string CppCrashTextFormatter::FormatCrashInfo()
{
return "";
}
bool CppCrashTextFormatter::NeedFormatCrashInfo()
{
return false;
}
#endif
ICrashFormatter& CppCrashFormatterFactory::Create()
{
#ifndef is_ohos_lite
static CppCrashJsonFormatter instance;
#else
static CppCrashTextFormatter instance;
#endif
return instance;
}
}
}