6#include <QLoggingCategory>
9#ifdef HAVE_RABBITCOMMON_GUI
12#include <QDesktopServices>
14#include <QApplication>
22#include "StackWalker.h"
24#include "CoreDump/MiniDumper.h"
25#elif defined(Q_OS_ANDROID)
34#include "RabbitCommonTools.h"
36#include "StackTrace.h"
40const char* g_PattenFileLine =
"%p [%s] in %s:%lu";
41const char* g_PattenDLL =
"%p [%s] in %s";
43static Q_LOGGING_CATEGORY(log,
"RabbitCommon.StackTrace")
45CCallTrace::CCallTrace(QObject *parent)
47 , m_hMainThread(QThread::currentThreadId())
50#ifdef HAVE_RABBITCOMMON_GUI
51void CCallTrace::ShowCoreDialog(QString szTitle, QString szContent, QString szDetail, QString szCoreDumpFile)
53 if(QThread::currentThreadId() == m_hMainThread) {
54 qDebug(log) <<
"Main thread:" << m_hMainThread <<
"core";
55 return slotShowCoreDialog(szTitle, szContent, szDetail, szCoreDumpFile);
57 qDebug(log) <<
"Thread:" << QThread::currentThreadId() <<
"core."
58 <<
"main thread:" << m_hMainThread;
60 check = connect(
this, SIGNAL(sigShowCoreDialog(QString,QString,QString,QString)),
61 this, SLOT(slotShowCoreDialog(QString,QString,QString,QString)),
62 Qt::BlockingQueuedConnection);
64 emit sigShowCoreDialog(szTitle, szContent, szDetail, szCoreDumpFile);
67void CCallTrace::slotShowCoreDialog(QString szTitle, QString szContent,
68 QString szDetail, QString szCoreDumpFile)
70 qDebug(log) <<
"CCallTrace::slotShowCoreDialog";
71 QMessageBox msg(QMessageBox::Icon::Critical, szTitle, szContent, QMessageBox::StandardButton::Close);
72 QPushButton* pOpenLogFile = msg.addButton(QObject::tr(
"Open log file"), QMessageBox::ActionRole);
73 QPushButton* pOpenCoreDumpFolder = msg.addButton(QObject::tr(
"Open core dump folder"), QMessageBox::ActionRole);
74#ifndef QT_NO_CLIPBOARD
75 QPushButton* pCopyClipboard = msg.addButton(tr(
"Copy to clipboard"), QMessageBox::ActionRole);
77 if(!szDetail.isEmpty())
78 msg.setDetailedText(szDetail);
80 if(msg.clickedButton() == pOpenLogFile)
83 }
else if (msg.clickedButton() == pOpenCoreDumpFolder) {
84 QFileInfo info(szCoreDumpFile);
85 QDesktopServices::openUrl(QUrl::fromLocalFile(info.absolutePath()));
86#ifndef QT_NO_CLIPBOARD
87 }
else if(msg.clickedButton() == pCopyClipboard) {
88 QClipboard* cb = QGuiApplication::clipboard();
90 qCritical(log) <<
"The application has not clipboard";
93 QMimeData* m =
new QMimeData();
95 qCritical(log) <<
"new QMimeData fail";
99 lstUrl << CLog::Instance()->GetLogFile() << szCoreDumpFile;
102 qDebug(log) <<
"Clipboard urls" << cb->mimeData()->urls();
105 qDebug(log) <<
"CCallTrace::slotShowCoreDialog end";
109QString CCallTrace::GetStack(uint index)
111 QString szMsg(
"Stack:\n");
112 QStringList szTrace = GetStack(index, 63);
113 for(
int i = 0; i < szTrace.length(); i++) {
114 szMsg +=
" " + QString::number(i + 1) +
" " + szTrace[i] +
"\n";
122#define TRACE_MAX_STACK_FRAMES 62
123QStringList CCallTrace::GetStack(uint index,
unsigned int max_frames)
126 const int nLen = 1024;
127 void *stack[TRACE_MAX_STACK_FRAMES];
128 HANDLE process = GetCurrentProcess();
129 SymInitialize(process, NULL, TRUE);
130 WORD numberOfFrames = CaptureStackBackTrace(index, TRACE_MAX_STACK_FRAMES, stack, NULL);
131 char buf[
sizeof(SYMBOL_INFO) + (nLen - 1) *
sizeof(TCHAR)];
132 SYMBOL_INFO* symbol = (SYMBOL_INFO*)buf;
133 symbol->MaxNameLen = nLen;
134 symbol->SizeOfStruct =
sizeof(SYMBOL_INFO);
136 IMAGEHLP_LINE64 line;
137 line.SizeOfStruct =
sizeof(IMAGEHLP_LINE64);
139 QScopedArrayPointer<char> bufStack(
new char[nLen]);
142 qCritical(log) <<
"new buffer fail";
145 for (
int i = 0; i < numberOfFrames; i++)
147 DWORD64 address = (DWORD64)(stack[i]);
148 SymFromAddr(process, address, NULL, symbol);
149 if (SymGetLineFromAddr64(process, address, &displacement, &line))
151 snprintf(bufStack.data(), nLen, g_PattenFileLine,
152 (LPVOID)symbol->Address, symbol->Name, line.FileName, line.LineNumber);
157 const int MaxNameLen = 256;
158 QScopedArrayPointer<char> module(
new char[MaxNameLen]);
159 HMODULE hModule = NULL;
160 lstrcpyA(module.data(),
"");
161 GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
162 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
163 (LPCTSTR) address, &hModule);
167 GetModuleFileNameA(hModule, module.data(), MaxNameLen);
169 snprintf(bufStack.data(), nLen, g_PattenDLL,
170 (LPVOID)symbol->Address, symbol->Name, module.data());
172 szStack << bufStack.data();
180static void MyStrCpy(
char* szDest,
size_t nMaxDestSize,
const char* szSrc)
182 if (nMaxDestSize <= 0)
184 strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE);
187 szDest[nMaxDestSize - 1] = 0;
189class MyStackWalker :
public StackWalker
192 MyStackWalker(QStringList *lstText,
194 int options = OptionsAll,
195 LPCSTR szSymPath = NULL,
196 DWORD dwProcessId = GetCurrentProcessId(),
197 HANDLE hProcess = GetCurrentProcess())
198 : StackWalker(options, szSymPath, dwProcessId, hProcess)
203 QStringList *m_lstText;
207 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
override;
208 virtual void OnOutput(LPCSTR szText)
override
212void MyStackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
219 CHAR buffer[STACKWALK_MAX_NAMELEN];
220 size_t maxLen = STACKWALK_MAX_NAMELEN;
224 if ((eType != lastEntry) && (entry.offset != 0))
226 if (entry.name[0] == 0)
227 MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN,
"(function-name not available)");
228 if (entry.undName[0] != 0)
229 MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName);
230 if (entry.undFullName[0] != 0)
231 MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName);
232 if (entry.moduleName[0] == 0)
233 MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN,
"(module-name not available)");
234 if (entry.lineFileName[0] == 0)
236 MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN,
"(filename not available)");
238 _snprintf_s(buffer, maxLen,
"%s in [%s] (%s:%d) address: %p",
239 entry.name, entry.moduleName,
240 entry.lineFileName, entry.lineNumber,
241 (LPVOID)entry.offset);
242 buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
243 *m_lstText << buffer;
247QStringList PrintStackTrace1(uint index,
unsigned int max_frames)
249 QStringList lstStack;
250 MyStackWalker sw(&lstStack, index, StackWalker::RetrieveSymbol);
256#elif defined(Q_OS_ANDROID)
259static _Unwind_Reason_Code unwindCallback(
struct _Unwind_Context* context,
void* arg)
261 std::vector<_Unwind_Word> &stack = *(std::vector<_Unwind_Word>*)arg;
262 stack.push_back(_Unwind_GetIP(context));
263 return _URC_NO_REASON;
266QStringList CCallTrace::GetStack(uint index,
unsigned int max_frames)
268 QStringList lstStack;
270 std::vector<_Unwind_Word> stack;
271 _Unwind_Backtrace(unwindCallback, (
void*)&stack);
273 int nBufferSize = 1024;
274 QScopedArrayPointer<char> buffer(
new char[nBufferSize]);
277 qCritical(log) <<
"new buffer fail";
280 for (
int i = index; i < stack.size(); i++) {
282 if (!dladdr((
void*)stack[i], &info)) {
285 int addr = (
char*)stack[i] - (
char*)info.dli_fbase - 1;
286 if (info.dli_sname == NULL || strlen(info.dli_sname) == 0) {
287 sprintf(buffer.data(),
"%p [%s]", addr, info.dli_fname);
289 sprintf(buffer.data(),
"%p [%s] in %s",
290 addr, info.dli_sname, info.dli_fname);
292 lstStack << buffer.data();
301QStringList CCallTrace::GetStack(uint index,
unsigned int max_frames)
305 int nBufferSize = 1024;
306 QScopedArrayPointer<char> buffer(
new char[nBufferSize]);
309 qCritical(log) <<
"new buffer fail";
314 void* addrlist[max_frames + 1];
317 int addrlen = backtrace(addrlist,
sizeof(addrlist) /
sizeof(
void*));
319 qCritical(log) <<
"Get backtrace is empty, possibly corrupt";
332 char** symbollist = backtrace_symbols(addrlist, addrlen);
335 size_t funcnamesize = 256;
336 char* funcname = (
char*)malloc(funcnamesize);
340 for (
int i = index; i < addrlen; i++)
342 char *begin_name = 0, *begin_offset = 0, *end_offset = 0;
346 for (
char *p = symbollist[i]; *p; ++p)
352 else if (*p ==
')' && begin_offset) {
358 if (begin_name && begin_offset && end_offset
359 && begin_name < begin_offset)
361 *begin_name++ =
'\0';
362 *begin_offset++ =
'\0';
370 char* ret = abi::__cxa_demangle(begin_name,
371 funcname, &funcnamesize, &status);
375 snprintf(buffer.data(), nBufferSize,
"%p [%s] in %s",
376 begin_offset, funcname, symbollist[i]);
380 snprintf(buffer.data(), nBufferSize,
"%p [%s] in %s",
381 begin_offset, begin_name, symbollist[i]);
387 snprintf(buffer.data(), nBufferSize,
"%s", symbollist[i]);
389 szMsg << buffer.data();