diff --git a/src/kdbgwin/CMakeLists.txt b/src/kdbgwin/CMakeLists.txt index a1ca4ed5..ba0feeec 100644 --- a/src/kdbgwin/CMakeLists.txt +++ b/src/kdbgwin/CMakeLists.txt @@ -1,28 +1,28 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) set(kdbgwin_SRCS common.h main.cpp - process.h - process.cpp + kdbgwin_process.h + kdbgwin_process.cpp abstract_generator.h abstract_generator.cpp callbacks.h callbacks.cpp outputters.h outputters.cpp ${CMAKE_CURRENT_BINARY_DIR}/../drkonqi_debug.cpp ) set(COMMON_LIBS dbghelp psapi shlwapi Qt5::Core) if ( MINGW ) add_executable(kdbgwin ${kdbgwin_SRCS} mingw_generator.h mingw_generator.cpp) target_link_libraries(kdbgwin PRIVATE ${COMMON_LIBS} bfd iberty intl ZLIB::ZLIB) endif () if ( MSVC OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) add_executable(kdbgwin ${kdbgwin_SRCS} msvc_generator.h msvc_generator.cpp) target_link_libraries(kdbgwin ${COMMON_LIBS}) endif () install(TARGETS kdbgwin DESTINATION ${KDE_INSTALL_LIBEXECDIR}) diff --git a/src/kdbgwin/abstract_generator.h b/src/kdbgwin/abstract_generator.h index 9222cf33..3c762cad 100644 --- a/src/kdbgwin/abstract_generator.h +++ b/src/kdbgwin/abstract_generator.h @@ -1,123 +1,123 @@ /****************************************************************** * * kdbgwin - Helper application for DrKonqi * * This file is part of the KDE project * * Copyright (C) 2010 Ilie Halip * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see *****************************************************************/ #pragma once #include "common.h" -#include "process.h" +#include "kdbgwin_process.h" #include const static char* BACKTRACE_FORMAT = "%1!%2() [%3 @ %4] at 0x%5"; // module.dll!KClass::function() [c:\file.cpp @ 10] at 0x0001000 const static char* DEFAULT_MODULE = "[unknown]"; const static char* DEFAULT_FUNC = "[unknown]"; const static char* DEFAULT_FILE = "[unknown]"; const static int DEFAULT_LINE = -1; /** * \brief Base generator class * * This class gives the definition of a backtrace generator. There are 2 subclasses: one for * MSVC and one for MinGW. The reason why implementation differs is the fact that executables * use different debugging formats: PDBs (for MSVC) and Dwarf-2/Stabs and possibly more I don't * know too much about for MinGW, which are embedded in the executable itself. */ class AbstractBTGenerator : public QObject { Q_OBJECT protected: /// A Process instance, corresponding to the process for which we generate the backtrace Process m_process; /// The current stack frame. It is kept as a member STACKFRAME64 m_currentFrame; /// The definition of a map of symbols typedef QMap TSymbolsMap; /// A map of symbols (the full path to the module, and a bool which specifies if /// symbols were loaded for it) TSymbolsMap m_symbolsMap; public: /// Constructor AbstractBTGenerator(const Process& process); virtual ~AbstractBTGenerator(); /// Abstract virtual: Initialize this generator virtual bool Init() = 0; /// Abstract virtual: Uninitialize this generator virtual void UnInit() = 0; /// Start generating the backtrace virtual void Run(HANDLE hTread, bool bFaultingThread); /// This method acts like a callback and will be called when the stack frame changed virtual void FrameChanged() = 0; /// Abstract virtual: get current module name /// @return the name of the current module (eg: kdecore.dll) virtual QString GetModuleName(); /// Abstract virtual: get current module path /// @return the full path to the current module (eg: N:\\kde\\bin\\kdecore.dll) virtual QString GetModulePath(); /// Abstract virtual: get current function/method name. The name is undecorated internally by this method. /// @return the current function or method (eg: KCmdLineArgs::args) virtual QString GetFunctionName() = 0; /// Abstract virtual: get the full path to the current file /// @return the path to the file (eg: N:\\kde\\svn\\KDE\\trunk\\kdelibs\\kcmdlineargs\\kcmdlineargs.cpp) virtual QString GetFile() = 0; /// Abstract virtual: get current line /// @return the current line in the file virtual int GetLine() = 0; /// Checks if symbols are loaded for the specified module /// @return true if symbols are loaded virtual bool IsSymbolLoaded(const QString& module); /// Tries to load symbols for all loaded modules virtual void LoadSymbols(); /// Tries to load a symbol file for a module loaded at dwBaseAddr virtual void LoadSymbol(const QString& module, DWORD64 dwBaseAddr) = 0; Q_SIGNALS: /// This will be emitted whenever the generator wishes to output information. It can either be /// module information (in the form: "Loaded C:\path\to.dll (symbols loaded)", a stack frame line, /// or newlines void DebugLine(const QString&); /// This signal is emitted when a module is loaded, and its symbols are missing. This is /// caught by the PackageSuggester void MissingSymbol(const QString&); /// Will be emitted when the generation finishes void Finished(); }; diff --git a/src/kdbgwin/process.cpp b/src/kdbgwin/kdbgwin_process.cpp similarity index 99% rename from src/kdbgwin/process.cpp rename to src/kdbgwin/kdbgwin_process.cpp index 2dbfa8b7..bc75aa67 100644 --- a/src/kdbgwin/process.cpp +++ b/src/kdbgwin/kdbgwin_process.cpp @@ -1,181 +1,181 @@ /****************************************************************** * * kdbgwin - Helper application for DrKonqi * * This file is part of the KDE project * * Copyright (C) 2010 Ilie Halip * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see *****************************************************************/ #include "common.h" -#include "process.h" +#include "kdbgwin_process.h" Process::Process() : m_bValid(FALSE) { } // we need debug privileges to open the proces with PROCESS_ALL_ACCESS, and // to successfully use ReadProcessMemory() BOOL Process::EnableDebugPrivilege() { qCDebug(DRKONQI_LOG) << "Enabling debug privilege"; HANDLE hToken = NULL; if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken)) { if (GetLastError() == ERROR_NO_TOKEN) { if (!ImpersonateSelf(SecurityImpersonation)) { qCCritical(DRKONQI_LOG) << "ImpersonateSelf() failed: " << GetLastError(); return FALSE; } if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken)) { qCCritical(DRKONQI_LOG) << "OpenThreadToken() #2 failed: " << GetLastError(); return FALSE; } } else { qCCritical(DRKONQI_LOG) << "OpenThreadToken() #1 failed: " << GetLastError(); return FALSE; } } LUID luid; if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { assert(false); qCCritical(DRKONQI_LOG) << "Cannot lookup privilege: " << GetLastError(); SafeCloseHandle(hToken); return FALSE; } TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; tp.Privileges[0].Luid = luid; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, NULL, (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL)) { assert(false); qCCritical(DRKONQI_LOG) << "Cannot adjust privilege: " << GetLastError(); SafeCloseHandle(hToken); return FALSE; } SafeCloseHandle(hToken); return TRUE; } BOOL Process::GetInfo(const char* pid, const char* threadId) { qCDebug(DRKONQI_LOG) << "Trying to get info about pid=" << pid; DWORD dwPid = DWORD(atoi(pid)); DWORD dwThread = DWORD(atoi(threadId)); // get handle to the process HANDLE hProcess = NULL; hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid); assert(hProcess); if (hProcess == NULL) { qCCritical(DRKONQI_LOG) << "Cannot open process " << dwPid << ": " << GetLastError(); return m_bValid; } m_dwPid = dwPid; m_hProcess = hProcess; m_dwThread = dwThread; TCHAR procPath[MAX_PATH * 2 + 1] = {0}; GetModuleFileNameEx(hProcess, NULL, procPath, MAX_PATH*2 + 1); m_path = QString::fromWCharArray(procPath); // we can't get the threads for a single process, so get all system's // threads, and enumerate through them HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL); if (hSnapshot == INVALID_HANDLE_VALUE) { qCCritical(DRKONQI_LOG) << "CreateToolhelp32Snapshot() failed: " << GetLastError(); assert(false); return m_bValid; } // get process threads THREADENTRY32 te; ZeroMemory(&te, sizeof(te)); te.dwSize = sizeof(te); if (Thread32First(hSnapshot, &te)) { do { if (te.th32OwnerProcessID == dwPid) { qCDebug(DRKONQI_LOG) << "Found thread " << te.th32ThreadID << ", adding to list"; HANDLE hThread = NULL; hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID); assert(hThread); if (hThread == NULL) { qCCritical(DRKONQI_LOG) << "Cannot open thread " << te.th32ThreadID << ": " << GetLastError(); continue; } m_threads[te.th32ThreadID] = hThread; // we have at least 1 thread, make this valid m_bValid = TRUE; } } while (Thread32Next(hSnapshot, &te)); } SafeCloseHandle(hSnapshot); assert(m_threads.size() > 0); // get process modules HMODULE hMods[1024]; DWORD cbNeeded = 0; if (!EnumProcessModules(hProcess, hMods, ArrayCount(hMods), &cbNeeded)) { qCCritical(DRKONQI_LOG) << "Cannot enumerate modules: " << GetLastError(); return m_bValid; } for (size_t i = 0; i < (cbNeeded / sizeof(hMods[0])); i++) { /* * In Windows, a wchar_t has 2 bytes; GCC defines wchar_t as int, * which is 4 bytes; so i can't use TCHAR here; better off using ushort * and casting when necessary */ ushort szModName[MAX_PATH]; if (GetModuleFileNameEx(hProcess, hMods[i], (LPTSTR) szModName, MAX_PATH)) { //QString str = QString::fromUtf16(szModName); //qCDebug(DRKONQI_LOG) << "Got module: " << str; //m_modules.push_back(QString::fromUtf16(szModName)); m_modules[QString::fromUtf16(szModName)] = hMods[i]; } } return m_bValid; } diff --git a/src/kdbgwin/process.h b/src/kdbgwin/kdbgwin_process.h similarity index 100% rename from src/kdbgwin/process.h rename to src/kdbgwin/kdbgwin_process.h diff --git a/src/kdbgwin/main.cpp b/src/kdbgwin/main.cpp index 840dbcae..be57c623 100644 --- a/src/kdbgwin/main.cpp +++ b/src/kdbgwin/main.cpp @@ -1,75 +1,75 @@ /****************************************************************** * * kdbgwin - Helper application for DrKonqi * * This file is part of the KDE project * * Copyright (C) 2010 Ilie Halip * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see *****************************************************************/ #include "msvc_generator.h" #include "mingw_generator.h" #include "outputters.h" -#include "process.h" +#include "kdbgwin_process.h" #include "common.h" #include int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(QStringLiteral("kdbgwin")); if (argc != 3) { qCCritical(DRKONQI_LOG) << "Parameters are incorrect"; return -1; } if (!Process::EnableDebugPrivilege()) { qCCritical(DRKONQI_LOG) << "Cannot enable debug privilege, exiting"; return -1; } // ok, argv[1] is the pid of the failing process, // and argv[2] the current thread id - let's get the info we need Process proc; if (!proc.GetInfo(argv[1], argv[2])) { qCCritical(DRKONQI_LOG) << "Cannot attach to process, exiting"; return -1; } #if defined(Q_CC_MSVC) MsvcGenerator generator(proc); #elif defined(Q_CC_GNU) MingwGenerator generator(proc); #endif Outputter outputter; QObject::connect(&generator, &MingwGenerator::DebugLine, &outputter, &Outputter::OnDebugLine); TThreadsMap::const_iterator it; for (it = proc.GetThreads().constBegin(); it != proc.GetThreads().constEnd(); it++) { generator.Run(it.value(), (it.key() == proc.GetThreadId())? true : false); } }