├── .gitignore ├── .gitreview ├── CMakeLists.txt ├── LICENSE.GPLv3 ├── app ├── CMakeLists.txt ├── app.pro ├── app.qbs ├── demangler.cpp ├── demangler.h ├── main.cpp ├── perfaddresscache.cpp ├── perfaddresscache.h ├── perfattributes.cpp ├── perfattributes.h ├── perfdata.cpp ├── perfdata.h ├── perfdwarfdiecache.cpp ├── perfdwarfdiecache.h ├── perfelfmap.cpp ├── perfelfmap.h ├── perfeucompat.h ├── perffeatures.cpp ├── perffeatures.h ├── perffilesection.cpp ├── perffilesection.h ├── perfheader.cpp ├── perfheader.h ├── perfkallsyms.cpp ├── perfkallsyms.h ├── perfregisterinfo.cpp ├── perfregisterinfo.h ├── perfstdin.cpp ├── perfstdin.h ├── perfsymboltable.cpp ├── perfsymboltable.h ├── perftracingdata.cpp ├── perftracingdata.h ├── perfunwind.cpp └── perfunwind.h ├── cmake ├── FindLibDDemangle.cmake ├── FindLibRustcDemangle.cmake └── FindZstd.cmake ├── elfutils.pri ├── paths.pri ├── perfparser.pro ├── perfparser.qbs └── tests ├── CMakeLists.txt ├── auto ├── CMakeLists.txt ├── addresscache │ ├── CMakeLists.txt │ ├── addresscache.pro │ ├── addresscache.qbs │ └── tst_addresscache.cpp ├── auto.pro ├── auto.qbs ├── elfmap │ ├── CMakeLists.txt │ ├── elfmap.pro │ ├── elfmap.qbs │ └── tst_elfmap.cpp ├── finddebugsym │ ├── CMakeLists.txt │ ├── finddebugsym.pro │ ├── finddebugsym.qbs │ └── tst_finddebugsym.cpp ├── kallsyms │ ├── CMakeLists.txt │ ├── kallsyms.pro │ ├── kallsyms.qbs │ └── tst_kallsyms.cpp ├── perfdata │ ├── CMakeLists.txt │ ├── contentsize.data │ ├── fork_static_gcc │ │ ├── fork.zlib │ │ ├── perf.data.zstd.expected.txt.zlib │ │ └── perf.data.zstd.zlib │ ├── parallel_static_gcc │ │ ├── parallel_static_gcc.zlib │ │ ├── perf.data.zstd.expected.txt.zlib │ │ └── perf.data.zstd.zlib │ ├── perfdata.pro │ ├── perfdata.qbs │ ├── perfdata.qrc │ ├── probe.data.file │ ├── probe.data.stream │ ├── tst_perfdata.cpp │ ├── vector_static_clang │ │ ├── perf.data.expected.txt.zlib │ │ ├── perf.data.zlib │ │ └── vector_static_clang_v8.0.1.zlib │ └── vector_static_gcc │ │ ├── perf.data.expected.txt.zlib │ │ ├── perf.data.zlib │ │ ├── perf.data.zstd.expected.txt.zlib │ │ ├── perf.data.zstd.zlib │ │ ├── perf.lbr.data.expected.txt.zlib │ │ ├── perf.lbr.data.zlib │ │ └── vector_static_gcc_v9.1.0.zlib ├── perfstdin │ ├── CMakeLists.txt │ ├── perfstdin.pro │ ├── perfstdin.qbs │ └── tst_perfstdin.cpp └── shared │ ├── perfparsertestclient.cpp │ ├── perfparsertestclient.h │ └── shared.pri ├── manual ├── CMakeLists.txt ├── clients │ ├── fork.c │ ├── parallel.cpp │ └── vector.cpp ├── manual.pro ├── manual.qbs └── perf2text │ ├── CMakeLists.txt │ ├── perf2text.cpp │ ├── perf2text.pro │ └── perf2text.qbs ├── tests.pro └── tests.qbs /.gitignore: -------------------------------------------------------------------------------- 1 | tests/auto/**/*.actual.txt 2 | tests/auto/perfdata/fork_static_gcc/fork 3 | tests/auto/perfdata/fork_static_gcc/perf.data.zstd 4 | tests/auto/perfdata/vector_static_clang/perf.data 5 | tests/auto/perfdata/vector_static_clang/vector_static_clang_v8.0.1 6 | tests/auto/perfdata/vector_static_gcc/perf.data 7 | tests/auto/perfdata/vector_static_gcc/perf.data.zstd 8 | tests/auto/perfdata/vector_static_gcc/perf.lbr.data 9 | tests/auto/perfdata/vector_static_gcc/vector_static_gcc_v9.1.0 10 | tests/auto/perfdata/parallel_static_gcc/parallel_static_gcc 11 | tests/auto/perfdata/parallel_static_gcc/perf.data.zstd 12 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=codereview.qt-project.org 3 | port=29418 4 | project=qt-creator/perfparser 5 | 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(elfutils) 2 | 3 | if (NOT elfutils_FOUND) 4 | message(STATUS "PerfParser is disabled. Set ELFUTILS_INSTALL_DIR to enable it.") 5 | return() 6 | endif() 7 | 8 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 9 | find_package(Zstd) 10 | 11 | find_package(LibRustcDemangle) 12 | set_package_properties(LibRustcDemangle PROPERTIES 13 | DESCRIPTION "Demangling for Rust symbols, written in Rust." 14 | PURPOSE "Demangling of Rust symbols" 15 | URL "https://github.com/alexcrichton/rustc-demangle" 16 | TYPE RUNTIME) 17 | 18 | find_package(LibDDemangle) 19 | set_package_properties(LibDDemangle PROPERTIES 20 | DESCRIPTION "Demangling for D symbols, written in D." 21 | PURPOSE "Demangling of D symbols" 22 | URL "https://github.com/lievenhey/d_demangler" 23 | TYPE RUNTIME) 24 | 25 | 26 | add_definitions(-DQT_NO_CAST_FROM_ASCII 27 | -DQT_NO_CAST_TO_ASCII 28 | -DQT_USE_QSTRINGBUILDER 29 | -DQT_NO_FOREACH) 30 | add_subdirectory(app) 31 | add_subdirectory(tests) 32 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_qtc_library(perfparser_lib STATIC 2 | ALLOW_ASCII_CASTS 3 | PUBLIC_DEPENDS 4 | Qt::Core 5 | Qt::Network 6 | elfutils::dw 7 | elfutils::elf 8 | PUBLIC_INCLUDES ./ 9 | SOURCES 10 | perfaddresscache.cpp 11 | perfattributes.cpp perfattributes.h 12 | perfheader.cpp perfheader.h 13 | perffilesection.cpp perffilesection.h 14 | perffeatures.cpp perffeatures.h 15 | perfdata.cpp perfdata.h 16 | perfunwind.cpp perfunwind.h 17 | perfregisterinfo.cpp perfregisterinfo.h 18 | perfstdin.cpp perfstdin.h 19 | perfsymboltable.cpp perfsymboltable.h 20 | perfelfmap.cpp perfelfmap.h 21 | perfkallsyms.cpp perfkallsyms.h 22 | perftracingdata.cpp perftracingdata.h 23 | perfdwarfdiecache.cpp perfdwarfdiecache.h 24 | perfeucompat.h 25 | demangler.cpp demangler.h 26 | ) 27 | 28 | if (Zstd_FOUND) 29 | target_include_directories(perfparser_lib PUBLIC ${Zstd_INCLUDE_DIR}) 30 | target_link_libraries(perfparser_lib PUBLIC ${Zstd_LIBRARY}) 31 | target_compile_definitions(perfparser_lib PUBLIC HAVE_ZSTD=1) 32 | endif() 33 | 34 | if (HAVE_DWFL_GET_DEBUGINFOD_CLIENT) 35 | target_link_libraries(perfparser_lib PRIVATE elfutils::debuginfod) 36 | target_compile_definitions(perfparser_lib PRIVATE HAVE_DWFL_GET_DEBUGINFOD_CLIENT=1) 37 | endif() 38 | 39 | add_qtc_executable(perfparser 40 | DEPENDS 41 | perfparser_lib 42 | SOURCES 43 | main.cpp 44 | ) 45 | -------------------------------------------------------------------------------- /app/app.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2014-08-14T10:44:20 4 | # 5 | #------------------------------------------------- 6 | 7 | QT = core network 8 | CONFIG += c++11 console 9 | CONFIG -= app_bundle 10 | 11 | include(../paths.pri) 12 | include(../elfutils.pri) 13 | 14 | DESTDIR = $$PERFPARSER_APP_DESTDIR 15 | target.path = $$PERFPARSER_APP_INSTALLDIR 16 | INSTALLS += target 17 | 18 | TARGET = perfparser 19 | 20 | SOURCES += main.cpp \ 21 | perfaddresscache.cpp \ 22 | perfattributes.cpp \ 23 | perfheader.cpp \ 24 | perffilesection.cpp \ 25 | perffeatures.cpp \ 26 | perfdata.cpp \ 27 | perfunwind.cpp \ 28 | perfregisterinfo.cpp \ 29 | perfstdin.cpp \ 30 | perfsymboltable.cpp \ 31 | perfelfmap.cpp \ 32 | perfkallsyms.cpp \ 33 | perftracingdata.cpp \ 34 | perfdwarfdiecache.cpp 35 | 36 | HEADERS += \ 37 | perfaddresscache.h \ 38 | perfattributes.h \ 39 | perfheader.h \ 40 | perffilesection.h \ 41 | perffeatures.h \ 42 | perfdata.h \ 43 | perfunwind.h \ 44 | perfregisterinfo.h \ 45 | perfstdin.h \ 46 | perfsymboltable.h \ 47 | perfelfmap.h \ 48 | perfkallsyms.h \ 49 | perftracingdata.h \ 50 | perfdwarfdiecache.h \ 51 | perfeucompat.h 52 | 53 | OTHER_FILES += app.qbs 54 | -------------------------------------------------------------------------------- /app/app.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | import qbs.FileInfo 3 | 4 | QtcTool { 5 | name: "perfparser" 6 | 7 | Depends { name: "qtc" } 8 | 9 | Depends { name: "Qt.network" } 10 | 11 | cpp.defines: base.filter(function(def) { return def != "QT_RESTRICTED_CAST_FROM_ASCII"; }) 12 | cpp.includePaths: project.includePaths 13 | cpp.libraryPaths: project.libPaths 14 | cpp.dynamicLibraries: ["dw", "elf"] 15 | 16 | files: [ 17 | "main.cpp", 18 | "demangler.cpp", 19 | "demangler.h", 20 | "perfaddresscache.cpp", 21 | "perfaddresscache.h", 22 | "perfattributes.cpp", 23 | "perfattributes.h", 24 | "perfheader.cpp", 25 | "perfheader.h", 26 | "perffilesection.cpp", 27 | "perffilesection.h", 28 | "perffeatures.cpp", 29 | "perffeatures.h", 30 | "perfdata.cpp", 31 | "perfdata.h", 32 | "perfunwind.cpp", 33 | "perfunwind.h", 34 | "perfregisterinfo.cpp", 35 | "perfregisterinfo.h", 36 | "perfstdin.cpp", 37 | "perfstdin.h", 38 | "perfsymboltable.cpp", 39 | "perfsymboltable.h", 40 | "perfelfmap.cpp", 41 | "perfelfmap.h", 42 | "perfkallsyms.cpp", 43 | "perfkallsyms.h", 44 | "perftracingdata.cpp", 45 | "perftracingdata.h", 46 | "perfdwarfdiecache.cpp", 47 | "perfdwarfdiecache.h", 48 | "perfeucompat.h" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /app/demangler.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Lieven Hey 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include "demangler.h" 21 | 22 | #include 23 | #include 24 | 25 | /* Demangler specification 26 | * int demangler(const char* mangledSymbol, char* demangledBuffer, size_t bufferSize) 27 | * 28 | * size_t is platform dependent (4 bytes on 32 bit, 8 bytes on 64 bit) 29 | * */ 30 | 31 | namespace { 32 | bool startsWith(const char* string, const QByteArray& prefix) { 33 | return strcmp(string, prefix.constData()) == 0; 34 | } 35 | } 36 | 37 | Demangler::Demangler() 38 | { 39 | loadDemangleLib(QStringLiteral("rustc_demangle"), "rustc_demangle", QByteArrayLiteral("_R")); 40 | loadDemangleLib(QStringLiteral("d_demangle"), "demangle_symbol", QByteArrayLiteral("_D")); 41 | } 42 | 43 | bool Demangler::demangle(const char *mangledSymbol, char *demangleBuffer, size_t demangleBufferLength) 44 | { 45 | // fast path, some languages (like rust since 1.37 or d) share a common prefix 46 | // try these first 47 | for (const auto& demangler : std::as_const(m_demanglers)) { 48 | if (startsWith(mangledSymbol, demangler.prefix)) { 49 | if (demangler.demangler(mangledSymbol, demangleBuffer, demangleBufferLength)) { 50 | return true; 51 | } 52 | } 53 | } 54 | 55 | for (const auto& demangler : std::as_const(m_demanglers)) { 56 | if (demangler.demangler(mangledSymbol, demangleBuffer, demangleBufferLength)) { 57 | return true; 58 | } 59 | } 60 | return false; 61 | } 62 | 63 | void Demangler::loadDemangleLib(const QString &name, const char* function, const QByteArray& prefix) 64 | { 65 | QLibrary lib(name); 66 | if (!lib.load()) { 67 | qDebug("failed to load library %ls: %ls", qUtf16Printable(name), qUtf16Printable(lib.errorString())); 68 | return; 69 | } 70 | const auto rawSymbol = lib.resolve(function); 71 | if (!rawSymbol) { 72 | qDebug("failed to resolve %s function in library %ls: %ls", function, qUtf16Printable(lib.fileName()), 73 | qUtf16Printable(lib.errorString())); 74 | return; 75 | } 76 | 77 | m_demanglers.push_back({prefix, reinterpret_cast(rawSymbol)}); 78 | } 79 | -------------------------------------------------------------------------------- /app/demangler.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Lieven Hey 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #ifndef DEMANGLER_H 21 | #define DEMANGLER_H 22 | 23 | #include 24 | 25 | class Demangler 26 | { 27 | public: 28 | Demangler(); 29 | 30 | bool demangle(const char* mangledSymbol, char* demangleBuffer, size_t demangleBufferLength); 31 | 32 | private: 33 | void loadDemangleLib(const QString& name, const char* function, const QByteArray& prefix); 34 | 35 | using demangler_t = int (*) (const char*, char *, size_t); 36 | struct DemangleInfo { 37 | QByteArray prefix; 38 | demangler_t demangler; 39 | }; 40 | 41 | QList m_demanglers; 42 | }; 43 | 44 | #endif // DEMANGLER_H 45 | -------------------------------------------------------------------------------- /app/perfaddresscache.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include "perfaddresscache.h" 21 | 22 | #include "perfdwarfdiecache.h" 23 | 24 | #include 25 | 26 | namespace { 27 | quint64 relativeAddress(const PerfElfMap::ElfInfo& elf, quint64 addr) 28 | { 29 | Q_ASSERT(elf.isValid()); 30 | const auto elfAddr = elf.hasBaseAddr() ? elf.baseAddr : elf.addr; 31 | Q_ASSERT(elfAddr <= addr); 32 | Q_ASSERT((elf.addr + elf.length) > addr); 33 | return addr - elfAddr; 34 | } 35 | } 36 | 37 | PerfAddressCache::AddressCacheEntry PerfAddressCache::find(const PerfElfMap::ElfInfo& elf, quint64 addr, 38 | OffsetAddressCache *invalidAddressCache) const 39 | { 40 | if (elf.isValid()) 41 | return m_cache.value(elf.originalPath).value(relativeAddress(elf, addr)); 42 | else 43 | return invalidAddressCache->value(addr); 44 | } 45 | 46 | void PerfAddressCache::cache(const PerfElfMap::ElfInfo& elf, quint64 addr, 47 | PerfAddressCache::AddressCacheEntry entry, 48 | OffsetAddressCache *invalidAddressCache) 49 | { 50 | if (elf.isValid()) 51 | m_cache[elf.originalPath][relativeAddress(elf, addr)] = entry; 52 | else 53 | (*invalidAddressCache)[addr] = entry; 54 | } 55 | 56 | static bool operator<(const PerfAddressCache::SymbolCacheEntry &lhs, const PerfAddressCache::SymbolCacheEntry &rhs) 57 | { 58 | return lhs.offset < rhs.offset; 59 | } 60 | 61 | static bool operator==(const PerfAddressCache::SymbolCacheEntry &lhs, const PerfAddressCache::SymbolCacheEntry &rhs) 62 | { 63 | return lhs.offset == rhs.offset && lhs.size == rhs.size; 64 | } 65 | 66 | static bool operator<(const PerfAddressCache::SymbolCacheEntry &lhs, quint64 addr) 67 | { 68 | return lhs.offset < addr; 69 | } 70 | 71 | 72 | bool PerfAddressCache::hasSymbolCache(const QByteArray &filePath) const 73 | { 74 | return m_symbolCache.contains(filePath); 75 | } 76 | 77 | PerfAddressCache::SymbolCacheEntry PerfAddressCache::findSymbol(const QByteArray& filePath, quint64 relAddr) 78 | { 79 | auto &symbols = m_symbolCache[filePath]; 80 | auto it = std::lower_bound(symbols.begin(), symbols.end(), relAddr); 81 | 82 | // demangle symbols on demand instead of demangling all symbols directly 83 | // hopefully most of the symbols we won't ever encounter after all 84 | auto lazyDemangle = [](PerfAddressCache::SymbolCacheEntry& entry) { 85 | if (!entry.demangled) { 86 | entry.symname = demangle(entry.symname); 87 | entry.demangled = true; 88 | } 89 | return entry; 90 | }; 91 | 92 | if (it != symbols.end() && it->offset == relAddr) 93 | return lazyDemangle(*it); 94 | if (it == symbols.begin()) 95 | return {}; 96 | 97 | --it; 98 | 99 | if (it->offset <= relAddr && (it->offset + it->size > relAddr || (it->size == 0))) { 100 | return lazyDemangle(*it); 101 | } 102 | return {}; 103 | } 104 | 105 | void PerfAddressCache::setSymbolCache(const QByteArray &filePath, SymbolCache cache) 106 | { 107 | /* 108 | * use stable_sort to produce results that are comparable to what addr2line would 109 | * return when we have entries like this in the symtab: 110 | * 111 | * 000000000045a130 l F .text 0000000000000033 .hidden __memmove_avx_unaligned 112 | * 000000000045a180 l F .text 00000000000003d8 .hidden __memmove_avx_unaligned_erms 113 | * 000000000045a180 l F .text 00000000000003d8 .hidden __memcpy_avx_unaligned_erms 114 | * 000000000045a130 l F .text 0000000000000033 .hidden __memcpy_avx_unaligned 115 | * 116 | * here, addr2line would always find the first entry. we want to do the same 117 | */ 118 | 119 | std::stable_sort(cache.begin(), cache.end()); 120 | cache.erase(std::unique(cache.begin(), cache.end()), cache.end()); 121 | m_symbolCache[filePath] = cache; 122 | } 123 | 124 | PerfAddressCache::SymbolCache PerfAddressCache::extractSymbols(Dwfl_Module *module, quint64 elfStart, bool isArmArch) 125 | { 126 | PerfAddressCache::SymbolCache cache; 127 | 128 | const auto numSymbols = dwfl_module_getsymtab(module); 129 | for (int i = 0; i < numSymbols; ++i) { 130 | GElf_Sym sym; 131 | GElf_Addr symAddr; 132 | const auto symbol = dwfl_module_getsym_info(module, i, &sym, &symAddr, nullptr, nullptr, nullptr); 133 | if (symbol) { 134 | const quint64 start = alignedAddress(sym.st_value, isArmArch); 135 | cache.append({symAddr - elfStart, start, sym.st_size, symbol}); 136 | } 137 | } 138 | return cache; 139 | } 140 | -------------------------------------------------------------------------------- /app/perfaddresscache.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #ifndef PERFADDRESSCACHE_H 21 | #define PERFADDRESSCACHE_H 22 | 23 | #include 24 | #include 25 | 26 | #include "perfelfmap.h" 27 | 28 | #include 29 | 30 | class PerfAddressCache 31 | { 32 | public: 33 | static quint64 symbolAddress(quint64 addr, bool isArmArch) 34 | { 35 | // For dwfl API call we need the raw pointer into symtab, so we need to adjust ip. 36 | return (!isArmArch || (addr & 1)) ? addr : addr + 1; 37 | } 38 | 39 | static quint64 alignedAddress(quint64 addr, bool isArmArch) 40 | { 41 | // Adjust addr back. The symtab entries are 1 off for all practical purposes. 42 | return (isArmArch && (addr & 1)) ? addr - 1 : addr; 43 | } 44 | 45 | struct AddressCacheEntry 46 | { 47 | AddressCacheEntry(int locationId = -1, bool isInterworking = false) 48 | : locationId(locationId) 49 | , isInterworking(isInterworking) 50 | {} 51 | bool isValid() const { return locationId >= 0; } 52 | int locationId; 53 | bool isInterworking; 54 | }; 55 | using OffsetAddressCache = QHash; 56 | 57 | struct SymbolCacheEntry 58 | { 59 | SymbolCacheEntry(quint64 offset = 0, quint64 value = 0, quint64 size = 0, const QByteArray &symname = {}) 60 | : offset(offset) 61 | , value(value) 62 | , size(size) 63 | , symname(symname) 64 | {} 65 | 66 | bool isValid() const { return !symname.isEmpty(); } 67 | 68 | // adjusted/absolute st_value, see documentation of the `addr` arg in `dwfl_module_getsym_info` 69 | quint64 offset; 70 | // unadjusted/relative st_value 71 | quint64 value; 72 | quint64 size; 73 | QByteArray symname; 74 | bool demangled = false; 75 | }; 76 | using SymbolCache = QVector; 77 | 78 | AddressCacheEntry find(const PerfElfMap::ElfInfo& elf, quint64 addr, 79 | OffsetAddressCache *invalidAddressCache) const; 80 | void cache(const PerfElfMap::ElfInfo& elf, quint64 addr, 81 | AddressCacheEntry entry, OffsetAddressCache *invalidAddressCache); 82 | 83 | /// check if @c setSymbolCache was called for @p filePath already 84 | bool hasSymbolCache(const QByteArray &filePath) const; 85 | /// take @p cache, sort it and use it for symbol lookups in @p filePath 86 | void setSymbolCache(const QByteArray &filePath, SymbolCache cache); 87 | /// find the symbol that encompasses @p relAddr in @p filePath 88 | /// if the found symbol wasn't yet demangled, it will be demangled now 89 | SymbolCacheEntry findSymbol(const QByteArray &filePath, quint64 relAddr); 90 | 91 | /// extract all symbols in @p module into a structure suitable to be passed to @p setSymbols 92 | static SymbolCache extractSymbols(Dwfl_Module *module, quint64 elfStart, bool isArmArch); 93 | 94 | private: 95 | QHash m_cache; 96 | QHash m_symbolCache; 97 | }; 98 | 99 | QT_BEGIN_NAMESPACE 100 | Q_DECLARE_TYPEINFO(PerfAddressCache::SymbolCacheEntry, Q_MOVABLE_TYPE); 101 | QT_END_NAMESPACE 102 | #endif 103 | -------------------------------------------------------------------------------- /app/perfattributes.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perfattributes.h" 22 | #include "perfdata.h" 23 | 24 | #include 25 | 26 | PerfEventAttributes::PerfEventAttributes() 27 | { 28 | memset(static_cast(this), 0, sizeof(PerfEventAttributes)); 29 | } 30 | 31 | QDataStream &operator>>(QDataStream &stream, PerfEventAttributes &attrs) 32 | { 33 | quint64 flags; 34 | stream >> attrs.m_type >> attrs.m_size; 35 | 36 | if (attrs.m_size < PerfEventAttributes::SIZE_VER0) { 37 | qWarning() << "unsupported file format. event attr size too small:" << attrs.m_size; 38 | return stream; 39 | } 40 | 41 | stream >> attrs.m_config >> attrs.m_samplePeriod >> attrs.m_sampleType >> attrs.m_readFormat 42 | >> flags >> attrs.m_wakeupEvents >> attrs.m_bpType >> attrs.m_bpAddr; 43 | 44 | if (attrs.m_size > PerfEventAttributes::SIZE_VER0) 45 | stream >> attrs.m_bpLen; 46 | 47 | if (attrs.m_size > PerfEventAttributes::SIZE_VER1) 48 | stream >> attrs.m_branchSampleType; 49 | 50 | if (attrs.m_size > PerfEventAttributes::SIZE_VER2) 51 | stream >> attrs.m_sampleRegsUser >> attrs.m_sampleStackUser >> attrs.m_clockid; 52 | 53 | if (attrs.m_size > PerfEventAttributes::SIZE_VER3) 54 | stream >> attrs.m_sampleRegsIntr; 55 | 56 | if (attrs.m_size > PerfEventAttributes::SIZE_VER4) 57 | stream >> attrs.m_auxWatermark >> attrs.m_sampleMaxStack >> attrs.m_reserved2; 58 | 59 | if (static_cast(stream.byteOrder()) != QSysInfo::ByteOrder) { 60 | // bit fields are saved in byte order; who came up with that BS? 61 | quint64 newFlags = 0; 62 | for (int i = 0; i < 64; ++i) { 63 | if ((flags & (1ull << i)) != 0) 64 | newFlags |= (1ull << (i / 8 + 7 - (i % 8))); 65 | } 66 | flags = newFlags; 67 | } 68 | 69 | *(&attrs.m_readFormat + 1) = flags; 70 | 71 | if (attrs.m_size > PerfEventAttributes::SIZE_VER5) { 72 | static const int intMax = std::numeric_limits::max(); 73 | quint32 skip = attrs.m_size - PerfEventAttributes::SIZE_VER5; 74 | if (skip > intMax) { 75 | stream.skipRawData(intMax); 76 | skip -= intMax; 77 | } 78 | stream.skipRawData(static_cast(skip)); 79 | } 80 | 81 | return stream; 82 | } 83 | 84 | int PerfEventAttributes::sampleIdOffset() const 85 | { 86 | int offset = 0; 87 | 88 | if (m_sampleType & SAMPLE_IDENTIFIER) 89 | return 0; 90 | 91 | if (!(m_sampleType & SAMPLE_ID)) 92 | return -1; 93 | 94 | if (m_sampleType & SAMPLE_IP) 95 | offset += sizeof(quint64); // PerfRecordSample::m_ip 96 | 97 | if (m_sampleType & SAMPLE_TID) 98 | offset += sizeof(quint32) + sizeof(quint32); // PerfRecordSampleId::{m_pid|m_tid} 99 | 100 | if (m_sampleType & SAMPLE_TIME) 101 | offset += sizeof(quint64); // PerfSampleId::m_time 102 | 103 | if (m_sampleType & SAMPLE_ADDR) 104 | offset += sizeof(quint64); // PerfRecordSample::m_addr 105 | 106 | return offset; 107 | } 108 | 109 | QByteArray PerfEventAttributes::name() const 110 | { 111 | switch (m_type) { 112 | case TYPE_HARDWARE: { 113 | switch (m_config) { 114 | case HARDWARE_CPU_CYCLES: return QByteArrayLiteral("cpu-cycles"); 115 | case HARDWARE_INSTRUCTIONS: return QByteArrayLiteral("instructions"); 116 | case HARDWARE_CACHE_REFERENCES: return QByteArrayLiteral("cache-references"); 117 | case HARDWARE_CACHE_MISSES: return QByteArrayLiteral("cache-misses"); 118 | case HARDWARE_BRANCH_INSTRUCTIONS: return QByteArrayLiteral("branch-instructions"); 119 | case HARDWARE_BRANCH_MISSES: return QByteArrayLiteral("branch-misses"); 120 | case HARDWARE_BUS_CYCLES: return QByteArrayLiteral("bus-cycles"); 121 | case HARDWARE_STALLED_CYCLES_FRONTEND: return QByteArrayLiteral("stalled-cycles-frontend"); 122 | case HARDWARE_STALLED_CYCLES_BACKEND: return QByteArrayLiteral("stalled-cycles-backend"); 123 | case HARDWARE_REF_CPU_CYCLES: return QByteArrayLiteral("ref-cycles"); 124 | default: return QByteArrayLiteral("hardware event: 0x") + QByteArray::number(m_config, 16); 125 | } 126 | } 127 | case TYPE_SOFTWARE: { 128 | switch (m_config) { 129 | case SOFTWARE_CPU_CLOCK: return QByteArrayLiteral("cpu-clock"); 130 | case SOFTWARE_TASK_CLOCK: return QByteArrayLiteral("task-clock"); 131 | case SOFTWARE_PAGE_FAULTS: return QByteArrayLiteral("page-faults"); 132 | case SOFTWARE_CONTEXT_SWITCHES: return QByteArrayLiteral("context-switches"); 133 | case SOFTWARE_CPU_MIGRATIONS: return QByteArrayLiteral("cpu-migrations"); 134 | case SOFTWARE_PAGE_FAULTS_MIN: return QByteArrayLiteral("minor-faults"); 135 | case SOFTWARE_PAGE_FAULTS_MAJ: return QByteArrayLiteral("major-faults"); 136 | case SOFTWARE_ALIGNMENT_FAULTS: return QByteArrayLiteral("alignment-faults"); 137 | case SOFTWARE_EMULATION_FAULTS: return QByteArrayLiteral("emulation-faults"); 138 | case SOFTWARE_DUMMY: return QByteArrayLiteral("dummy"); 139 | default: return QByteArrayLiteral("software event: 0x") + QByteArray::number(m_config, 16); 140 | } 141 | } 142 | case TYPE_TRACEPOINT: 143 | return QByteArrayLiteral("tracepoint: 0x") + QByteArray::number(m_config, 16); 144 | case TYPE_HARDWARE_CACHE: { 145 | QByteArray result; 146 | switch (m_config & 0xff) { 147 | case HARDWARE_CACHE_L1D: result += QByteArrayLiteral("L1-dcache"); break; 148 | case HARDWARE_CACHE_L1I: result += QByteArrayLiteral("L1-icache"); break; 149 | case HARDWARE_CACHE_LL: result += QByteArrayLiteral("LLC"); break; 150 | case HARDWARE_CACHE_DTLB: result += QByteArrayLiteral("dTLB"); break; 151 | case HARDWARE_CACHE_ITLB: result += QByteArrayLiteral("iTLB"); break; 152 | case HARDWARE_CACHE_BPU: result += QByteArrayLiteral("branch"); break; 153 | case HARDWARE_CACHE_NODE: result += QByteArrayLiteral("node"); break; 154 | default: return QByteArrayLiteral("hardware cache event: 0x") 155 | + QByteArray::number(m_config, 16); 156 | } 157 | switch ((m_config >> 8) & 0xff) { 158 | case HARDWARE_CACHE_OPERATION_READ: result += QByteArrayLiteral("-load"); break; 159 | case HARDWARE_CACHE_OPERATION_WRITE: result += QByteArrayLiteral("-store"); break; 160 | case HARDWARE_CACHE_OPERATION_PREFETCH: result += QByteArrayLiteral("-prefetch"); break; 161 | default: return result + QByteArrayLiteral(" event: 0x") + QByteArray::number(m_config, 16); 162 | } 163 | switch ((m_config >> 16) & 0xff) { 164 | case HARDWARE_CACHE_RESULT_OPERATION_ACCESS: return result + QByteArrayLiteral("-refs"); 165 | case HARDWARE_CACHE_RESULT_OPERATION_MISS: return result + QByteArrayLiteral("-misses"); 166 | default: return result + QByteArrayLiteral(" event: 0x") + QByteArray::number(m_config, 16); 167 | }; 168 | } 169 | case TYPE_RAW: 170 | return QByteArrayLiteral("raw event: 0x") + QByteArray::number(m_config, 16); 171 | case TYPE_BREAKPOINT: 172 | return QByteArrayLiteral("breakpoint: 0x") + QByteArray::number(m_config, 16); 173 | default: 174 | return QByteArrayLiteral("unknown event ") + QByteArray::number(m_type) 175 | + QByteArrayLiteral(": 0x") + QByteArray::number(m_config, 16); 176 | } 177 | } 178 | 179 | bool PerfEventAttributes::operator==(const PerfEventAttributes &rhs) const 180 | { 181 | return m_type == rhs.m_type 182 | && m_size == rhs.m_size 183 | && m_config == rhs.m_config 184 | && m_samplePeriod == rhs.m_samplePeriod 185 | && m_sampleType == rhs.m_sampleType 186 | && m_readFormat == rhs.m_readFormat 187 | && m_disabled == rhs.m_disabled 188 | && m_inherit == rhs.m_inherit 189 | && m_pinned == rhs.m_pinned 190 | && m_exclusive == rhs.m_exclusive 191 | && m_excludeUser == rhs.m_excludeUser 192 | && m_excludeKernel == rhs.m_excludeKernel 193 | && m_excludeHv == rhs.m_excludeHv 194 | && m_excludeIdle == rhs.m_excludeIdle 195 | && m_mmap == rhs.m_mmap 196 | && m_comm == rhs.m_comm 197 | && m_freq == rhs.m_freq 198 | && m_inheritStat == rhs.m_inheritStat 199 | && m_enableOnExec == rhs.m_enableOnExec 200 | && m_task == rhs.m_task 201 | && m_watermark == rhs.m_watermark 202 | && m_preciseIp == rhs.m_preciseIp 203 | && m_mmapData == rhs.m_mmapData 204 | && m_sampleIdAll == rhs.m_sampleIdAll 205 | && m_excludeHost == rhs.m_excludeHost 206 | && m_excludeGuest == rhs.m_excludeGuest 207 | && m_excludeCallchainKernel == rhs.m_excludeCallchainKernel 208 | && m_excludeCallchainUser == rhs.m_excludeCallchainUser 209 | && m_reserved1 == rhs.m_reserved1 210 | && m_wakeupEvents == rhs.m_wakeupEvents 211 | && m_bpType == rhs.m_bpType 212 | && m_bpAddr == rhs.m_bpAddr 213 | && m_bpLen == rhs.m_bpLen 214 | && m_branchSampleType == rhs.m_branchSampleType 215 | && m_sampleRegsUser == rhs.m_sampleRegsUser 216 | && m_sampleStackUser == rhs.m_sampleStackUser 217 | && m_clockid == rhs.m_clockid 218 | && m_sampleRegsIntr == rhs.m_sampleRegsIntr 219 | && m_auxWatermark == rhs.m_auxWatermark 220 | && m_sampleMaxStack == rhs.m_sampleMaxStack 221 | && m_reserved2 == rhs.m_reserved2; 222 | } 223 | 224 | bool PerfAttributes::read(QIODevice *device, PerfHeader *header) 225 | { 226 | if (header->attrSize() < PerfEventAttributes::SIZE_VER0 + PerfFileSection::fixedLength()) { 227 | qWarning() << "unsupported file format: header attrSize too small:" << header->attrSize(); 228 | return false; 229 | } 230 | 231 | PerfEventAttributes attrs; 232 | PerfFileSection ids; 233 | 234 | for (uint i = 0; i < header->numAttrs(); ++i) { 235 | 236 | if (!device->seek(header->attrs().offset + header->attrSize() * i)) { 237 | qWarning() << "cannot seek to attribute section" << i 238 | << header->attrs().offset + header->attrSize() * i; 239 | return false; 240 | } 241 | 242 | QDataStream stream(device); 243 | stream.setByteOrder(header->byteOrder()); 244 | stream >> attrs; 245 | 246 | if (attrs.size() < PerfEventAttributes::SIZE_VER0) 247 | return false; 248 | 249 | if (i == 0) 250 | m_globalAttributes = attrs; 251 | 252 | stream >> ids; 253 | if (ids.size > 0) { 254 | if (!device->seek(ids.offset)) { 255 | qWarning() << "cannot seek to attribute ID section"; 256 | return false; 257 | } 258 | 259 | QDataStream idStream(device); 260 | idStream.setByteOrder(header->byteOrder()); 261 | quint64 id; 262 | for (qint64 i = 0, num = ids.size / static_cast(sizeof(quint64)); 263 | i < num; ++i) { 264 | idStream >> id; 265 | m_attributes[id] = attrs; 266 | } 267 | } 268 | 269 | } 270 | return true; 271 | } 272 | 273 | void PerfAttributes::setGlobalAttributes(const PerfEventAttributes &attributes) 274 | { 275 | m_globalAttributes = attributes; 276 | } 277 | 278 | void PerfAttributes::addAttributes(quint64 id, const PerfEventAttributes &attributes) 279 | { 280 | m_attributes[id] = attributes; 281 | } 282 | 283 | const PerfEventAttributes &PerfAttributes::attributes(quint64 id) const 284 | { 285 | QHash::ConstIterator i = m_attributes.find(id); 286 | if (i != m_attributes.end()) 287 | return i.value(); 288 | else 289 | return m_globalAttributes; 290 | } 291 | -------------------------------------------------------------------------------- /app/perfattributes.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include "perffilesection.h" 24 | #include "perfheader.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | class PerfEventAttributes { 31 | public: 32 | enum Sizes { 33 | SIZE_VER0 = 64, /* sizeof first published struct */ 34 | SIZE_VER1 = 72, /* add: config2 */ 35 | SIZE_VER2 = 80, /* add: branch_sample_type */ 36 | SIZE_VER3 = 96, /* add: sample_regs_user, sample_stack_user */ 37 | SIZE_VER4 = 104, /* add: sample_regs_intr */ 38 | SIZE_VER5 = 112, /* add: aux_watermark */ 39 | }; 40 | 41 | PerfEventAttributes(); 42 | 43 | bool sampleIdAll() const { return m_sampleIdAll; } 44 | quint64 sampleType() const { return m_sampleType; } 45 | quint64 readFormat() const { return m_readFormat; } 46 | quint64 sampleRegsUser() const { return m_sampleRegsUser; } 47 | quint32 size() const { return m_size; } 48 | quint32 type() const { return m_type; } 49 | quint64 config() const { return m_config; } 50 | int sampleIdOffset() const; 51 | bool usesFrequency() const { return m_freq; }; 52 | quint64 frequenyOrPeriod() const { return m_sampleFreq; } 53 | 54 | QByteArray name() const; 55 | 56 | enum ReadFormat { 57 | FORMAT_TOTAL_TIME_ENABLED = 1U << 0, 58 | FORMAT_TOTAL_TIME_RUNNING = 1U << 1, 59 | FORMAT_ID = 1U << 2, 60 | FORMAT_GROUP = 1U << 3, 61 | 62 | FORMAT_MAX = 1U << 4 63 | }; 64 | 65 | /* 66 | * Bits that can be set in sampleType to request information 67 | * in the overflow packets. 68 | */ 69 | enum SampleFormat { 70 | SAMPLE_IP = 1U << 0, 71 | SAMPLE_TID = 1U << 1, 72 | SAMPLE_TIME = 1U << 2, 73 | SAMPLE_ADDR = 1U << 3, 74 | SAMPLE_READ = 1U << 4, 75 | SAMPLE_CALLCHAIN = 1U << 5, 76 | SAMPLE_ID = 1U << 6, 77 | SAMPLE_CPU = 1U << 7, 78 | SAMPLE_PERIOD = 1U << 8, 79 | SAMPLE_STREAM_ID = 1U << 9, 80 | SAMPLE_RAW = 1U << 10, 81 | SAMPLE_BRANCH_STACK = 1U << 11, 82 | SAMPLE_REGS_USER = 1U << 12, 83 | SAMPLE_STACK_USER = 1U << 13, 84 | SAMPLE_WEIGHT = 1U << 14, 85 | SAMPLE_DATA_SRC = 1U << 15, 86 | SAMPLE_IDENTIFIER = 1U << 16, 87 | SAMPLE_TRANSACTION = 1U << 17, 88 | 89 | SAMPLE_MAX = 1U << 18, 90 | SAMPLE_ID_ALL = 1U << 31 // extra flag, to check if the sample has a sample ID at all 91 | }; 92 | 93 | /* 94 | * attr.type() 95 | */ 96 | enum TypeId { 97 | TYPE_HARDWARE = 0, 98 | TYPE_SOFTWARE = 1, 99 | TYPE_TRACEPOINT = 2, 100 | TYPE_HARDWARE_CACHE = 3, 101 | TYPE_RAW = 4, 102 | TYPE_BREAKPOINT = 5, 103 | TYPE_MAX, /* non-ABI */ 104 | }; 105 | 106 | /* 107 | * Generalized performance event eventId types, used by the 108 | * attr.event_id parameter of the sys_perf_event_open() 109 | * syscall. 110 | * 111 | * Ends up in attr.config() if type() is TYPE_HARDWARE 112 | */ 113 | enum HardwareId { 114 | /* 115 | * Common hardware events, generalized by the kernel: 116 | */ 117 | HARDWARE_CPU_CYCLES = 0, 118 | HARDWARE_INSTRUCTIONS = 1, 119 | HARDWARE_CACHE_REFERENCES = 2, 120 | HARDWARE_CACHE_MISSES = 3, 121 | HARDWARE_BRANCH_INSTRUCTIONS = 4, 122 | HARDWARE_BRANCH_MISSES = 5, 123 | HARDWARE_BUS_CYCLES = 6, 124 | HARDWARE_STALLED_CYCLES_FRONTEND = 7, 125 | HARDWARE_STALLED_CYCLES_BACKEND = 8, 126 | HARDWARE_REF_CPU_CYCLES = 9, 127 | HARDWARE_MAX, /* non-ABI */ 128 | }; 129 | 130 | /* 131 | * Generalized hardware cache events: 132 | * 133 | * attr.config() for type() == TYPE_HW_CACHE. 134 | * 135 | * Encoding is (cacheId | (cacheOpId << 8) | (cacheOpResultId << 16)) 136 | * for example -e L1-dcache-store-misses results in config == 0x10100, or 137 | * -e LLC-loads in config == 0x000002. 138 | */ 139 | enum HardwareCacheId { 140 | HARDWARE_CACHE_L1D = 0, 141 | HARDWARE_CACHE_L1I = 1, 142 | HARDWARE_CACHE_LL = 2, 143 | HARDWARE_CACHE_DTLB = 3, 144 | HARDWARE_CACHE_ITLB = 4, 145 | HARDWARE_CACHE_BPU = 5, 146 | HARDWARE_CACHE_NODE = 6, 147 | 148 | HARDWARE_CACHE_MAX, /* non-ABI */ 149 | }; 150 | 151 | enum HardwareCacheOperationId { 152 | HARDWARE_CACHE_OPERATION_READ = 0, 153 | HARDWARE_CACHE_OPERATION_WRITE = 1, 154 | HARDWARE_CACHE_OPERATION_PREFETCH = 2, 155 | HARDWARE_CACHE_OPERATION_MAX, /* non-ABI */ 156 | }; 157 | 158 | enum HardwareCacheOperationResultId { 159 | HARDWARE_CACHE_RESULT_OPERATION_ACCESS = 0, 160 | HARDWARE_CACHE_RESULT_OPERATION_MISS = 1, 161 | HARDWARE_CACHE_RESULT_OPERATION_MAX, /* non-ABI */ 162 | }; 163 | 164 | /* 165 | * Special "software" events provided by the kernel, even if the hardware 166 | * does not support performance events. These events measure various 167 | * physical and sw events of the kernel (and allow the profiling of them as 168 | * well): 169 | */ 170 | enum SoftwareId { 171 | SOFTWARE_CPU_CLOCK = 0, 172 | SOFTWARE_TASK_CLOCK = 1, 173 | SOFTWARE_PAGE_FAULTS = 2, 174 | SOFTWARE_CONTEXT_SWITCHES = 3, 175 | SOFTWARE_CPU_MIGRATIONS = 4, 176 | SOFTWARE_PAGE_FAULTS_MIN = 5, 177 | SOFTWARE_PAGE_FAULTS_MAJ = 6, 178 | SOFTWARE_ALIGNMENT_FAULTS = 7, 179 | SOFTWARE_EMULATION_FAULTS = 8, 180 | SOFTWARE_DUMMY = 9, 181 | SOFTWARE_MAX, /* non-ABI */ 182 | }; 183 | 184 | bool operator==(const PerfEventAttributes &rhs) const; 185 | 186 | private: 187 | 188 | /* 189 | * Major type: hardware/software/tracepoint/etc. 190 | */ 191 | quint32 m_type; 192 | 193 | /* 194 | * Size of the attr structure, for fwd/bwd compat. 195 | */ 196 | quint32 m_size; 197 | 198 | /* 199 | * Type specific configuration information. 200 | */ 201 | quint64 m_config; 202 | 203 | union { 204 | quint64 m_samplePeriod; 205 | quint64 m_sampleFreq; 206 | }; 207 | 208 | quint64 m_sampleType; 209 | quint64 m_readFormat; 210 | 211 | quint64 m_disabled : 1, /* off by default */ 212 | m_inherit : 1, /* children inherit it */ 213 | m_pinned : 1, /* must always be on PMU */ 214 | m_exclusive : 1, /* only group on PMU */ 215 | m_excludeUser : 1, /* don't count user */ 216 | m_excludeKernel : 1, /* ditto kernel */ 217 | m_excludeHv : 1, /* ditto hypervisor */ 218 | m_excludeIdle : 1, /* don't count when idle */ 219 | m_mmap : 1, /* include mmap data */ 220 | m_comm : 1, /* include comm data */ 221 | m_freq : 1, /* use freq, not period */ 222 | m_inheritStat : 1, /* per task counts */ 223 | m_enableOnExec : 1, /* next exec enables */ 224 | m_task : 1, /* trace fork/exit */ 225 | m_watermark : 1, /* wakeup_watermark */ 226 | /* 227 | * m_preciseIp: 228 | * 229 | * 0 - SAMPLE_IP can have arbitrary skid 230 | * 1 - SAMPLE_IP must have constant skid 231 | * 2 - SAMPLE_IP requested to have 0 skid 232 | * 3 - SAMPLE_IP must have 0 skid 233 | * 234 | * See also PERF_RECORD_MISC_EXACT_IP 235 | */ 236 | m_preciseIp : 2, /* skid constraint */ 237 | m_mmapData : 1, /* non-exec mmap data */ 238 | m_sampleIdAll : 1, /* sample_type all events */ 239 | 240 | m_excludeHost : 1, /* don't count in host */ 241 | m_excludeGuest : 1, /* don't count in guest */ 242 | 243 | m_excludeCallchainKernel : 1, /* exclude kernel callchains */ 244 | m_excludeCallchainUser : 1, /* exclude user callchains */ 245 | 246 | m_reserved1 : 41; 247 | 248 | union { 249 | quint32 m_wakeupEvents; /* wakeup every n events */ 250 | quint32 m_wakeupWatermark; /* bytes before wakeup */ 251 | }; 252 | 253 | quint32 m_bpType; 254 | union { 255 | quint64 m_bpAddr; 256 | quint64 m_config1; /* extension of config */ 257 | }; 258 | 259 | union { 260 | quint64 m_bpLen; 261 | quint64 m_config2; /* extension of config1 */ 262 | }; 263 | 264 | quint64 m_branchSampleType; /* enum perf_branch_sample_type */ 265 | 266 | /* 267 | * Defines set of user regs to dump on samples. 268 | * See asm/perf_regs.h for details. 269 | */ 270 | quint64 m_sampleRegsUser; 271 | 272 | /* 273 | * Defines size of the user stack to dump on samples. 274 | */ 275 | quint32 m_sampleStackUser; 276 | 277 | qint32 m_clockid; 278 | 279 | /* 280 | * Defines set of regs to dump for each sample 281 | * state captured on: 282 | * - precise = 0: PMU interrupt 283 | * - precise > 0: sampled instruction 284 | * 285 | * See asm/perf_regs.h for details. 286 | */ 287 | quint64 m_sampleRegsIntr; 288 | 289 | /* 290 | * Wakeup watermark for AUX area 291 | */ 292 | quint32 m_auxWatermark; 293 | quint16 m_sampleMaxStack; 294 | 295 | /* Align to u64. */ 296 | quint16 m_reserved2; 297 | 298 | friend QDataStream &operator>>(QDataStream &stream, PerfEventAttributes &attrs); 299 | }; 300 | 301 | QDataStream &operator>>(QDataStream &stream, PerfEventAttributes &attrs); 302 | 303 | class PerfAttributes { 304 | public: 305 | bool read(QIODevice *device, PerfHeader *header); 306 | void addAttributes(quint64 id, const PerfEventAttributes &attributes); 307 | void setGlobalAttributes(const PerfEventAttributes &attributes); 308 | 309 | const QHash &attributes() const { return m_attributes; } 310 | const PerfEventAttributes &attributes(quint64 id) const; 311 | const PerfEventAttributes &globalAttributes() const { return m_globalAttributes; } 312 | 313 | private: 314 | PerfEventAttributes m_globalAttributes; 315 | QHash m_attributes; 316 | }; 317 | -------------------------------------------------------------------------------- /app/perfdwarfdiecache.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | /// @return the demangled symbol name 30 | QByteArray demangle(const QByteArray &mangledName); 31 | 32 | struct DwarfRange 33 | { 34 | Dwarf_Addr low; 35 | Dwarf_Addr high; 36 | 37 | bool contains(Dwarf_Addr addr) const 38 | { 39 | return low <= addr && addr < high; 40 | } 41 | }; 42 | 43 | /// cache of dwarf ranges for a given Dwarf_Die 44 | struct DieRanges 45 | { 46 | Dwarf_Die die; 47 | QList ranges; 48 | 49 | bool contains(Dwarf_Addr addr) const 50 | { 51 | return std::any_of(ranges.begin(), ranges.end(), [addr](DwarfRange range) { 52 | return range.contains(addr); 53 | }); 54 | } 55 | }; 56 | 57 | /// cache of sub program DIE, its ranges and the accompanying die name 58 | class SubProgramDie 59 | { 60 | public: 61 | SubProgramDie() = default; 62 | SubProgramDie(Dwarf_Die die); 63 | ~SubProgramDie(); 64 | 65 | bool isEmpty() const { return m_ranges.ranges.isEmpty(); } 66 | /// @p offset a bias-corrected offset 67 | bool contains(Dwarf_Addr offset) const { return m_ranges.contains(offset); } 68 | Dwarf_Die *die() { return &m_ranges.die; } 69 | 70 | private: 71 | DieRanges m_ranges; 72 | }; 73 | 74 | /// cache of dwarf ranges for a CU DIE and child sub programs 75 | class CuDieRangeMapping 76 | { 77 | public: 78 | CuDieRangeMapping() = default; 79 | CuDieRangeMapping(Dwarf_Die cudie, Dwarf_Addr bias); 80 | ~CuDieRangeMapping(); 81 | 82 | bool isEmpty() const { return m_cuDieRanges.ranges.isEmpty(); } 83 | bool contains(Dwarf_Addr addr) const { return m_cuDieRanges.contains(addr); } 84 | Dwarf_Addr bias() { return m_bias; } 85 | Dwarf_Die *cudie() { return &m_cuDieRanges.die; } 86 | 87 | /// On first call this will visit the CU DIE to cache all subprograms 88 | /// @return the DW_TAG_subprogram DIE that contains @p offset 89 | /// @p offset a bias-corrected address to find a subprogram for 90 | SubProgramDie *findSubprogramDie(Dwarf_Addr offset); 91 | 92 | /// @return a fully qualified, demangled symbol name for @p die 93 | QByteArray dieName(Dwarf_Die *die); 94 | 95 | private: 96 | void addSubprograms(); 97 | 98 | Dwarf_Addr m_bias = 0; 99 | DieRanges m_cuDieRanges; 100 | QList m_subPrograms; 101 | QHash m_dieNameCache; 102 | }; 103 | 104 | /** 105 | * @return all DW_TAG_inlined_subroutine DIEs that contain @p offset 106 | * @p subprogram DIE sub tree that should be traversed to look for inlined scopes 107 | * @p offset bias-corrected address that is checked against the dwarf ranges of the DIEs 108 | */ 109 | QList findInlineScopes(Dwarf_Die *subprogram, Dwarf_Addr offset); 110 | 111 | /** 112 | * @return the absolute source path for a @p path that may be absolute already or relative to the compilation directory 113 | * @p path either an absolute that will be passed through directly or a path relative to the compilation directory 114 | * @p cuDie the CU DIE that will be queried for the compilation directory to resolve relative paths 115 | * @sa findSourceLocation 116 | */ 117 | QByteArray absoluteSourcePath(const char *path, Dwarf_Die *cuDie); 118 | 119 | struct DwarfSourceLocation 120 | { 121 | QByteArray file; 122 | int line = -1; 123 | int column = -1; 124 | 125 | explicit operator bool() const 126 | { 127 | return !file.isEmpty(); 128 | } 129 | }; 130 | /** 131 | * @return the absolute file name, line number and column for the instruction at the given @p offset in @p cuDie 132 | * @p cuDie CU DIE that should be queried 133 | * @p offset bias-corrected address of an instruction for which the information should be found 134 | * @sa CuDieRangeMapping 135 | */ 136 | DwarfSourceLocation findSourceLocation(Dwarf_Die* cuDie, Dwarf_Addr offset); 137 | 138 | /** 139 | * This cache makes it easily possible to find a CU DIE (i.e. Compilation Unit Debugging Information Entry) 140 | * based on a 141 | */ 142 | class PerfDwarfDieCache 143 | { 144 | public: 145 | PerfDwarfDieCache(Dwfl_Module *mod = nullptr); 146 | ~PerfDwarfDieCache(); 147 | 148 | /// @p addr absolute address, not bias-corrected 149 | CuDieRangeMapping *findCuDie(Dwarf_Addr addr); 150 | 151 | public: 152 | QList m_cuDieRanges; 153 | }; 154 | QT_BEGIN_NAMESPACE 155 | Q_DECLARE_TYPEINFO(DwarfRange, Q_MOVABLE_TYPE); 156 | Q_DECLARE_TYPEINFO(PerfDwarfDieCache, Q_MOVABLE_TYPE); 157 | Q_DECLARE_TYPEINFO(DieRanges, Q_MOVABLE_TYPE); 158 | Q_DECLARE_TYPEINFO(CuDieRangeMapping, Q_MOVABLE_TYPE); 159 | Q_DECLARE_TYPEINFO(Dwarf_Die, Q_MOVABLE_TYPE); 160 | QT_END_NAMESPACE 161 | -------------------------------------------------------------------------------- /app/perfelfmap.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perfdata.h" 22 | #include "perfelfmap.h" 23 | 24 | #include 25 | 26 | QDebug operator<<(QDebug stream, const PerfElfMap::ElfInfo& info) 27 | { 28 | stream.nospace() << "ElfInfo{" 29 | << "localFile=" << info.localFile.absoluteFilePath() << ", " 30 | << "isFile=" << info.isFile() << ", " 31 | << "originalFileName=" << info.originalFileName << ", " 32 | << "originalPath=" << info.originalPath << ", " 33 | << "addr=" << Qt::hex << info.addr << ", " 34 | << "len=" << Qt::hex << info.length << ", " 35 | << "pgoff=" << Qt::hex << info.pgoff << ", " 36 | << "baseAddr="; 37 | 38 | if (info.hasBaseAddr()) 39 | stream << Qt::hex << info.baseAddr; 40 | else 41 | stream << "n/a"; 42 | 43 | stream << "}"; 44 | return stream.space(); 45 | } 46 | 47 | namespace { 48 | struct SortByAddr 49 | { 50 | bool operator()(const PerfElfMap::ElfInfo &lhs, const quint64 addr) const 51 | { 52 | return lhs.addr < addr; 53 | } 54 | 55 | bool operator()(const quint64 addr, const PerfElfMap::ElfInfo &rhs) const 56 | { 57 | return addr < rhs.addr; 58 | } 59 | }; 60 | } 61 | 62 | PerfElfMap::PerfElfMap(QObject *parent) 63 | : QObject(parent) 64 | { 65 | } 66 | 67 | PerfElfMap::~PerfElfMap() = default; 68 | 69 | void PerfElfMap::registerElf(quint64 addr, quint64 len, quint64 pgoff, 70 | const QFileInfo &fullPath, const QByteArray &originalFileName, 71 | const QByteArray &originalPath) 72 | { 73 | quint64 addrEnd = addr + len; 74 | 75 | QVarLengthArray newElfs; 76 | QVarLengthArray removedElfs; 77 | for (auto i = m_elfs.begin(), end = m_elfs.end(); i != end && i->addr < addrEnd; ++i) { 78 | const quint64 iEnd = i->addr + i->length; 79 | if (iEnd < addr) 80 | continue; 81 | 82 | if (addr - pgoff == i->addr - i->pgoff && originalPath == i->originalPath) { 83 | // Remapping parts of the same file in the same place: Extend to maximum continuous 84 | // address range and check if we already have that. 85 | addr = qMin(addr, i->addr); 86 | pgoff = qMin(pgoff, i->pgoff); 87 | addrEnd = qMax(addrEnd, iEnd); 88 | len = addrEnd - addr; 89 | if (addr == i->addr && len == i->length) { 90 | // New mapping is fully contained in old one: Nothing to do. 91 | Q_ASSERT(newElfs.isEmpty()); 92 | Q_ASSERT(removedElfs.isEmpty()); 93 | return; 94 | } 95 | } else if (iEnd == addr) { 96 | // Directly adjacent sections of the same file can be merged. Ones of different files 97 | // don't bother each other. 98 | continue; 99 | } 100 | 101 | // Newly added elf overwrites existing one. Mark the existing one as overwritten and 102 | // reinsert any fragments of it that remain. 103 | 104 | if (i->addr < addr) { 105 | newElfs.push_back(ElfInfo(i->localFile, i->addr, addr - i->addr, i->pgoff, 106 | i->originalFileName, i->originalPath)); 107 | } 108 | if (iEnd > addrEnd) { 109 | newElfs.push_back(ElfInfo(i->localFile, addrEnd, iEnd - addrEnd, 110 | i->pgoff + addrEnd - i->addr, 111 | i->originalFileName, i->originalPath)); 112 | } 113 | 114 | emit aboutToInvalidate(*i); 115 | removedElfs.push_back(static_cast(std::distance(m_elfs.begin(), i))); 116 | } 117 | 118 | // remove the overwritten elfs, iterate from the back to not invalidate the indices 119 | for (auto it = removedElfs.rbegin(), end = removedElfs.rend(); it != end; ++it) 120 | m_elfs.remove(*it); 121 | 122 | ElfInfo elf(fullPath, addr, len, pgoff, originalFileName, originalPath); 123 | 124 | if (elf.isFile()) { 125 | if (m_lastBase.originalPath == originalPath && elf.addr > m_lastBase.addr) 126 | elf.baseAddr = m_lastBase.addr; 127 | else if (!pgoff) 128 | m_lastBase = elf; 129 | else 130 | m_lastBase = ElfInfo(); 131 | } 132 | 133 | newElfs.push_back(elf); 134 | 135 | for (const auto &elf : newElfs) { 136 | auto it = std::lower_bound(m_elfs.begin(), m_elfs.end(), 137 | elf.addr, SortByAddr()); 138 | m_elfs.insert(it, elf); 139 | } 140 | } 141 | 142 | PerfElfMap::ElfInfo PerfElfMap::findElf(quint64 ip) const 143 | { 144 | auto i = std::upper_bound(m_elfs.begin(), m_elfs.end(), ip, SortByAddr()); 145 | Q_ASSERT (i == m_elfs.constEnd() || i->addr != ip); 146 | if (i != m_elfs.constBegin()) 147 | --i; 148 | else 149 | return ElfInfo(); 150 | 151 | if (i->dwflStart < i->dwflEnd) 152 | return (i->dwflStart <= ip && i->dwflEnd > ip) ? *i : ElfInfo(); 153 | else 154 | return (i->addr + i->length > ip) ? *i : ElfInfo(); 155 | } 156 | 157 | void PerfElfMap::updateElf(quint64 addr, quint64 dwflStart, quint64 dwflEnd) 158 | { 159 | auto i = std::upper_bound(m_elfs.begin(), m_elfs.end(), addr, SortByAddr()); 160 | Q_ASSERT(i != m_elfs.begin()); 161 | --i; 162 | Q_ASSERT(i->addr == addr); 163 | i->dwflStart = dwflStart; 164 | i->dwflEnd = dwflEnd; 165 | } 166 | 167 | bool PerfElfMap::isAddressInRange(quint64 addr) const 168 | { 169 | if (m_elfs.isEmpty()) 170 | return false; 171 | 172 | const auto &first = m_elfs.first(); 173 | const auto &last = m_elfs.last(); 174 | return first.addr <= addr && addr < (last.addr + last.length); 175 | } 176 | -------------------------------------------------------------------------------- /app/perfelfmap.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | class PerfElfMap : public QObject 28 | { 29 | Q_OBJECT 30 | public: 31 | struct ElfInfo { 32 | enum { 33 | INVALID_BASE_ADDR = std::numeric_limits::max() 34 | }; 35 | explicit ElfInfo(const QFileInfo &localFile = QFileInfo(), quint64 addr = 0, 36 | quint64 length = 0, quint64 pgoff = 0, 37 | const QByteArray &originalFileName = {}, 38 | const QByteArray &originalPath = {}) : 39 | localFile(localFile), 40 | originalFileName(originalFileName.isEmpty() 41 | ? localFile.fileName().toLocal8Bit() 42 | : originalFileName), 43 | originalPath(originalPath.isEmpty() 44 | ? localFile.absoluteFilePath().toLocal8Bit() 45 | : originalPath), 46 | addr(addr), length(length), pgoff(pgoff) 47 | {} 48 | 49 | bool isValid() const 50 | { 51 | return length > 0; 52 | } 53 | 54 | bool isFile() const 55 | { 56 | return localFile.isFile(); 57 | } 58 | 59 | bool hasBaseAddr() const 60 | { 61 | return baseAddr != INVALID_BASE_ADDR; 62 | } 63 | 64 | bool operator==(const ElfInfo& rhs) const 65 | { 66 | return isFile() == rhs.isFile() 67 | && (!isFile() || localFile == rhs.localFile) 68 | && originalFileName == rhs.originalFileName 69 | && originalPath == rhs.originalPath 70 | && addr == rhs.addr 71 | && length == rhs.length 72 | && pgoff == rhs.pgoff 73 | && baseAddr == rhs.baseAddr; 74 | } 75 | 76 | bool operator!=(const ElfInfo& rhs) const 77 | { 78 | return !operator==(rhs); 79 | } 80 | 81 | QFileInfo localFile; 82 | QByteArray originalFileName; 83 | QByteArray originalPath; 84 | quint64 addr; 85 | quint64 length; 86 | quint64 pgoff; 87 | quint64 baseAddr = INVALID_BASE_ADDR; 88 | quint64 dwflStart = 0; 89 | quint64 dwflEnd = 0; 90 | }; 91 | 92 | explicit PerfElfMap(QObject *parent = nullptr); 93 | ~PerfElfMap(); 94 | 95 | void registerElf(quint64 addr, quint64 len, quint64 pgoff, 96 | const QFileInfo &fullPath, 97 | const QByteArray &originalFileName = {}, 98 | const QByteArray &originalPath = {}); 99 | ElfInfo findElf(quint64 ip) const; 100 | void updateElf(quint64 addr, quint64 dwflStart, quint64 dwflEnd); 101 | 102 | bool isEmpty() const 103 | { 104 | return m_elfs.isEmpty(); 105 | } 106 | 107 | bool isAddressInRange(quint64 addr) const; 108 | 109 | void copyDataFrom(const PerfElfMap *parent) 110 | { 111 | m_elfs = parent->m_elfs; 112 | m_lastBase = parent->m_lastBase; 113 | } 114 | 115 | signals: 116 | void aboutToInvalidate(const PerfElfMap::ElfInfo &elf); 117 | 118 | private: 119 | // elf sorted by start address 120 | QList m_elfs; 121 | // last registered elf with zero pgoff 122 | ElfInfo m_lastBase; 123 | }; 124 | 125 | QT_BEGIN_NAMESPACE 126 | Q_DECLARE_TYPEINFO(PerfElfMap::ElfInfo, Q_MOVABLE_TYPE); 127 | QT_END_NAMESPACE 128 | 129 | QDebug operator<<(QDebug stream, const PerfElfMap::ElfInfo& info); 130 | -------------------------------------------------------------------------------- /app/perfeucompat.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2020 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of Qt Creator. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #pragma once 27 | 28 | #include 29 | 30 | #ifdef Q_OS_WIN 31 | #include 32 | #else 33 | #include 34 | #include 35 | #define eu_compat_open open 36 | #define eu_compat_close close 37 | #define eu_compat_malloc malloc 38 | #define eu_compat_free free 39 | #define eu_compat_demangle abi::__cxa_demangle 40 | #define eu_compat_strdup strdup 41 | #define O_BINARY 0 42 | #endif 43 | -------------------------------------------------------------------------------- /app/perffeatures.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perffeatures.h" 22 | 23 | #include 24 | 25 | #include 26 | 27 | // TODO: What to do if feature flags are set but features don't really exist in the file? 28 | 29 | static void removeTrailingZeros(QByteArray *string) 30 | { 31 | int length = string->length(); 32 | // chop off trailing zeros to make the values directly usable 33 | while (length > 0 && !string->at(length - 1)) 34 | --length; 35 | string->resize(length); 36 | } 37 | 38 | void PerfFeatures::createFeature(QIODevice *device, QDataStream::ByteOrder byteOrder, 39 | PerfFileSection section, PerfHeader::Feature featureId) 40 | { 41 | device->seek(section.offset); 42 | QDataStream stream(device); 43 | stream.setByteOrder(byteOrder); 44 | 45 | switch (featureId) { 46 | case PerfHeader::TRACING_DATA: 47 | if (section.size > std::numeric_limits::max()) { 48 | qWarning() << "Excessively large tracing data section" << section.size; 49 | } else if (section.size < 0) { 50 | qWarning() << "Tracing data section with negative size" << section.size; 51 | } else { 52 | m_tracingData.setSize(static_cast(section.size)); 53 | stream >> m_tracingData; 54 | } 55 | break; 56 | case PerfHeader::BUILD_ID: 57 | m_buildId.size = section.size; 58 | stream >> m_buildId; 59 | break; 60 | case PerfHeader::HOSTNAME: 61 | stream >> m_hostName; 62 | break; 63 | case PerfHeader::OSRELEASE: 64 | stream >> m_osRelease; 65 | break; 66 | case PerfHeader::VERSION: 67 | stream >> m_version; 68 | break; 69 | case PerfHeader::ARCH: 70 | stream >> m_arch; 71 | break; 72 | case PerfHeader::CPUDESC: 73 | stream >> m_cpuDesc; 74 | break; 75 | case PerfHeader::CPUID: 76 | stream >> m_cpuId; 77 | break; 78 | case PerfHeader::NRCPUS: 79 | stream >> m_nrCpus; 80 | break; 81 | case PerfHeader::TOTAL_MEM: 82 | stream >> m_totalMem; 83 | break; 84 | case PerfHeader::CMDLINE: 85 | stream >> m_cmdline; 86 | break; 87 | case PerfHeader::EVENT_DESC: 88 | stream >> m_eventDesc; 89 | break; 90 | case PerfHeader::CPU_TOPOLOGY: 91 | stream >> m_cpuTopology; 92 | break; 93 | case PerfHeader::NUMA_TOPOLOGY: 94 | stream >> m_numaToplogy; 95 | break; 96 | case PerfHeader::BRANCH_STACK: 97 | // Doesn't really exist 98 | break; 99 | case PerfHeader::PMU_MAPPINGS: 100 | stream >> m_pmuMappings; 101 | break; 102 | case PerfHeader::GROUP_DESC: 103 | stream >> m_groupDesc; 104 | break; 105 | case PerfHeader::COMPRESSED: 106 | stream >> m_compressed; 107 | break; 108 | default: 109 | qDebug() << "unhandled feature" << featureId << section.size; 110 | return; 111 | } 112 | 113 | qint64 readSize = device->pos() - section.offset; 114 | if (section.size != readSize) 115 | qWarning() << "feature not properly read" << featureId << section.size << readSize; 116 | } 117 | 118 | PerfFeatures::PerfFeatures() 119 | { 120 | } 121 | 122 | PerfFeatures::~PerfFeatures() 123 | { 124 | } 125 | 126 | bool PerfFeatures::read(QIODevice *device, const PerfHeader *header) 127 | { 128 | if (!device->seek(header->featureOffset())) { 129 | qWarning() << "cannot seek to" << header->featureOffset(); 130 | return false; 131 | } 132 | QDataStream stream(device); 133 | stream.setByteOrder(header->byteOrder()); 134 | 135 | QHash featureSections; 136 | PerfFileSection section; 137 | for (uint i = 0; i < PerfHeader::LAST_FEATURE; ++i) { 138 | PerfHeader::Feature feature = static_cast(i); 139 | if (header->hasFeature(feature)) { 140 | stream >> section; 141 | if (section.size > 0) 142 | featureSections[feature] = section; 143 | else 144 | qWarning() << "Feature announced in header but not present:" << feature; 145 | } 146 | } 147 | 148 | QHash::ConstIterator i; 149 | for (i = featureSections.constBegin(); i != featureSections.constEnd(); ++i) 150 | createFeature(device, stream.byteOrder(), i.value(), i.key()); 151 | 152 | return true; 153 | } 154 | 155 | QDataStream &operator>>(QDataStream &stream, PerfBuildId &buildId) 156 | { 157 | qint64 next = 0; 158 | while (next < buildId.size) { 159 | PerfEventHeader header; 160 | stream >> header; 161 | 162 | PerfBuildId::BuildId build; 163 | stream >> build.pid; 164 | 165 | build.id.resize(PerfBuildId::s_idLength); 166 | stream.readRawData(build.id.data(), PerfBuildId::s_idLength); 167 | stream.skipRawData(PerfBuildId::s_idPadding); 168 | 169 | quint16 fileNameLength = header.size - PerfEventHeader::fixedLength() - sizeof(build.pid) 170 | - PerfBuildId::s_idPadding - PerfBuildId::s_idLength; 171 | build.fileName.resize(fileNameLength); 172 | stream.readRawData(build.fileName.data(), fileNameLength); 173 | removeTrailingZeros(&build.fileName); 174 | 175 | next += header.size; 176 | buildId.buildIds << build; 177 | } 178 | return stream; 179 | } 180 | 181 | QDataStream &operator<<(QDataStream &stream, const PerfBuildId::BuildId &buildId) 182 | { 183 | return stream << buildId.pid << buildId.id << buildId.fileName; 184 | } 185 | 186 | QDataStream &operator>>(QDataStream &stream, PerfEventHeader &header) 187 | { 188 | return stream >> header.type >> header.misc >> header.size; 189 | } 190 | 191 | QDataStream &operator>>(QDataStream &stream, PerfStringFeature &string) 192 | { 193 | quint32 length; 194 | stream >> length; 195 | static const int intMax = std::numeric_limits::max(); 196 | if (length > intMax) { 197 | qWarning() << "Excessively long string" << length; 198 | stream.skipRawData(intMax); 199 | stream.skipRawData(static_cast(length - intMax)); 200 | return stream; 201 | } 202 | string.value.resize(static_cast(length)); 203 | stream.readRawData(string.value.data(), string.value.length()); 204 | removeTrailingZeros(&string.value); 205 | return stream; 206 | } 207 | 208 | QDataStream &operator>>(QDataStream &stream, PerfNrCpus &nrCpus) 209 | { 210 | return stream >> nrCpus.online >> nrCpus.available; 211 | } 212 | 213 | QDataStream &operator<<(QDataStream &stream, PerfNrCpus nrCpus) 214 | { 215 | return stream << nrCpus.online << nrCpus.available; 216 | } 217 | 218 | QDataStream &operator>>(QDataStream &stream, PerfTotalMem &totalMem) 219 | { 220 | return stream >> totalMem.totalMem; 221 | } 222 | 223 | QDataStream &operator>>(QDataStream &stream, PerfCmdline &cmdline) 224 | { 225 | quint32 numParts; 226 | stream >> numParts; 227 | PerfStringFeature stringFeature; 228 | while (numParts-- > 0) { 229 | stream >> stringFeature; 230 | cmdline.cmdline << stringFeature.value; 231 | } 232 | return stream; 233 | } 234 | 235 | QDataStream &operator>>(QDataStream &stream, PerfEventDesc &eventDesc) 236 | { 237 | quint32 numEvents; 238 | quint32 eventSize; 239 | quint32 numIds; 240 | quint64 id; 241 | stream >> numEvents >> eventSize; 242 | PerfStringFeature stringFeature; 243 | while (numEvents-- > 0) { 244 | eventDesc.eventDescs << PerfEventDesc::EventDesc(); 245 | PerfEventDesc::EventDesc ¤tEvent = eventDesc.eventDescs.last(); 246 | stream >> currentEvent.attrs; 247 | stream >> numIds; 248 | stream >> stringFeature; 249 | currentEvent.name = stringFeature.value; 250 | while (numIds-- > 0) { 251 | stream >> id; 252 | currentEvent.ids << id; 253 | } 254 | } 255 | // There is some additional length-encoded stuff after this, but perf doesn't read that, either. 256 | // On top of that perf is only interested in the event name and throws everything else away 257 | // after reading. 258 | return stream; 259 | } 260 | 261 | QDataStream &operator>>(QDataStream &stream, PerfCpuTopology &cpuTopology) 262 | { 263 | quint32 numSiblings; 264 | PerfStringFeature stringFeature; 265 | stream >> numSiblings; 266 | 267 | while (numSiblings-- > 0) { 268 | stream >> stringFeature; 269 | cpuTopology.siblingCores << stringFeature.value; 270 | } 271 | 272 | stream >> numSiblings; 273 | while (numSiblings-- > 0) { 274 | stream >> stringFeature; 275 | cpuTopology.siblingThreads << stringFeature.value; 276 | } 277 | return stream; 278 | } 279 | 280 | QDataStream &operator<<(QDataStream &stream, const PerfCpuTopology &cpuTopology) 281 | { 282 | return stream << cpuTopology.siblingCores << cpuTopology.siblingThreads; 283 | } 284 | 285 | QDataStream &operator>>(QDataStream &stream, PerfNumaTopology &numaTopology) 286 | { 287 | quint32 numNodes; 288 | stream >> numNodes; 289 | 290 | PerfStringFeature stringFeature; 291 | while (numNodes-- > 0) { 292 | PerfNumaTopology::NumaNode node; 293 | stream >> node.nodeId >> node.memTotal >> node.memFree >> stringFeature; 294 | node.topology = stringFeature.value; 295 | numaTopology.nodes << node; 296 | } 297 | return stream; 298 | } 299 | 300 | QDataStream &operator<<(QDataStream &stream, const PerfNumaTopology::NumaNode &node) 301 | { 302 | return stream << node.nodeId << node.memTotal << node.memFree << node.topology; 303 | } 304 | 305 | QDataStream &operator>>(QDataStream &stream, PerfPmuMappings &pmuMappings) 306 | { 307 | quint32 numPmus; 308 | stream >> numPmus; 309 | 310 | PerfStringFeature stringFeature; 311 | while (numPmus-- > 0) { 312 | PerfPmuMappings::Pmu pmu; 313 | stream >> pmu.type >> stringFeature; 314 | pmu.name = stringFeature.value; 315 | pmuMappings.pmus << pmu; 316 | } 317 | return stream; 318 | } 319 | 320 | QDataStream &operator<<(QDataStream &stream, const PerfPmuMappings::Pmu &pmu) 321 | { 322 | return stream << pmu.type << pmu.name; 323 | } 324 | 325 | QDataStream &operator>>(QDataStream &stream, PerfGroupDesc &groupDesc) 326 | { 327 | quint32 numGroups; 328 | stream >> numGroups; 329 | 330 | PerfStringFeature stringFeature; 331 | while (numGroups-- > 0) { 332 | PerfGroupDesc::GroupDesc desc; 333 | stream >> stringFeature; 334 | desc.name = stringFeature.value; 335 | stream >> desc.leaderIndex >> desc.numMembers; 336 | groupDesc.groupDescs << desc; 337 | } 338 | return stream; 339 | } 340 | 341 | QDataStream &operator<<(QDataStream &stream, const PerfGroupDesc::GroupDesc &groupDesc) 342 | { 343 | return stream << groupDesc.name << groupDesc.leaderIndex << groupDesc.numMembers; 344 | } 345 | 346 | 347 | QDataStream &operator>>(QDataStream &stream, PerfCompressed &compressed) 348 | { 349 | stream >> compressed.version; 350 | stream >> compressed.type; 351 | stream >> compressed.level; 352 | stream >> compressed.ratio; 353 | stream >> compressed.mmap_len; 354 | return stream; 355 | } 356 | -------------------------------------------------------------------------------- /app/perffeatures.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include "perfattributes.h" 24 | #include "perfheader.h" 25 | #include "perftracingdata.h" 26 | 27 | #include 28 | #include 29 | 30 | struct PerfEventHeader { 31 | PerfEventHeader() : type(0), misc(0), size(0) {} 32 | quint32 type; 33 | quint16 misc; 34 | quint16 size; 35 | 36 | static quint16 fixedLength() { return sizeof(type) + sizeof(misc) + sizeof(size); } 37 | }; 38 | 39 | QDataStream &operator>>(QDataStream &stream, PerfEventHeader &header); 40 | 41 | struct PerfBuildId { 42 | PerfBuildId() : size(0) {} 43 | 44 | static const quint16 s_idLength = 20; 45 | static const quint16 s_idPadding = 4; // 20 aligned to 8 gives 24 => 4 unused bytes 46 | static const quint16 s_pathMax = 4096; 47 | 48 | struct BuildId { 49 | qint32 pid; 50 | QByteArray id; // raw id, use .toHex() to get something human-readable 51 | QByteArray fileName; 52 | }; 53 | 54 | qint64 size; 55 | QList buildIds; 56 | }; 57 | 58 | QDataStream &operator>>(QDataStream &stream, PerfBuildId &buildId); 59 | QDataStream &operator<<(QDataStream &stream, const PerfBuildId::BuildId &buildId); 60 | 61 | struct PerfStringFeature { 62 | QByteArray value; 63 | }; 64 | 65 | QDataStream &operator>>(QDataStream &stream, PerfStringFeature &stringFeature); 66 | 67 | struct PerfNrCpus { 68 | quint32 online; 69 | quint32 available; 70 | }; 71 | 72 | QDataStream &operator>>(QDataStream &stream, PerfNrCpus &numCpus); 73 | QDataStream &operator<<(QDataStream &stream, PerfNrCpus numCpus); 74 | 75 | struct PerfTotalMem { 76 | quint64 totalMem; 77 | }; 78 | 79 | QDataStream &operator>>(QDataStream &stream, PerfTotalMem &totalMem); 80 | 81 | struct PerfCmdline { 82 | QList cmdline; 83 | }; 84 | 85 | QDataStream &operator>>(QDataStream &stream, PerfCmdline &cmdline); 86 | 87 | struct PerfEventDesc { 88 | struct EventDesc { 89 | PerfEventAttributes attrs; 90 | QByteArray name; 91 | QList ids; 92 | }; 93 | 94 | QList eventDescs; 95 | }; 96 | 97 | QDataStream &operator>>(QDataStream &stream, PerfEventDesc &eventDesc); 98 | 99 | struct PerfCpuTopology { 100 | 101 | // Some kind of bitmask. Not so important for now 102 | QList siblingCores; 103 | QList siblingThreads; 104 | }; 105 | 106 | QDataStream &operator>>(QDataStream &stream, PerfCpuTopology &cpuTopology); 107 | QDataStream &operator<<(QDataStream &stream, const PerfCpuTopology &cpuTopology); 108 | 109 | struct PerfNumaTopology { 110 | 111 | struct NumaNode { 112 | quint32 nodeId; 113 | quint64 memTotal; 114 | quint64 memFree; 115 | QByteArray topology; 116 | }; 117 | 118 | QList nodes; 119 | }; 120 | 121 | QDataStream &operator>>(QDataStream &stream, PerfNumaTopology &numaTopology); 122 | QDataStream &operator<<(QDataStream &stream, const PerfNumaTopology::NumaNode &numaNode); 123 | 124 | struct PerfPmuMappings { 125 | 126 | struct Pmu { 127 | quint32 type; 128 | QByteArray name; 129 | }; 130 | QList pmus; 131 | }; 132 | 133 | QDataStream &operator>>(QDataStream &stream, PerfPmuMappings &pmuMappings); 134 | QDataStream &operator<<(QDataStream &stream, const PerfPmuMappings::Pmu &pmu); 135 | 136 | struct PerfGroupDesc { 137 | 138 | struct GroupDesc { 139 | QByteArray name; 140 | quint32 leaderIndex; 141 | quint32 numMembers; 142 | }; 143 | 144 | QList groupDescs; 145 | }; 146 | 147 | QDataStream &operator>>(QDataStream &stream, PerfGroupDesc &groupDesc); 148 | QDataStream &operator<<(QDataStream &stream, const PerfGroupDesc::GroupDesc &groupDesc); 149 | 150 | struct PerfCompressed { 151 | quint32 version = 0; 152 | quint32 type = 0; 153 | quint32 level = 0; 154 | quint32 ratio = 0; 155 | quint32 mmap_len = 0; 156 | 157 | enum Type { 158 | PERF_COMP_NONE = 0, 159 | PERF_COMP_ZSTD, 160 | PERF_COMP_MAX 161 | }; 162 | Q_ENUM(Type) 163 | Q_GADGET 164 | }; 165 | 166 | QDataStream &operator>>(QDataStream &stream, PerfCompressed &compressed); 167 | 168 | class PerfFeatures 169 | { 170 | public: 171 | PerfFeatures(); 172 | ~PerfFeatures(); 173 | 174 | bool read(QIODevice *device, const PerfHeader *header); 175 | const QByteArray &architecture() const { return m_arch.value; } 176 | void setArchitecture(const QByteArray &arch) { m_arch.value = arch; } 177 | 178 | PerfTracingData tracingData() const { return m_tracingData; } 179 | QList buildIds() const { return m_buildId.buildIds; } 180 | QByteArray hostName() const { return m_hostName.value; } 181 | QByteArray osRelease() const { return m_osRelease.value; } 182 | QByteArray version() const { return m_version.value; } 183 | PerfNrCpus nrCpus() const { return m_nrCpus; } 184 | QByteArray cpuDesc() const { return m_cpuDesc.value; } 185 | QByteArray cpuId() const { return m_cpuId.value; } 186 | quint64 totalMem() const { return m_totalMem.totalMem; } 187 | QList cmdline() const { return m_cmdline.cmdline; } 188 | PerfEventDesc eventDesc() const { return m_eventDesc; } 189 | PerfCpuTopology cpuTopology() const { return m_cpuTopology; } 190 | QList numaTopology() const { return m_numaToplogy.nodes; } 191 | QList pmuMappings() const { return m_pmuMappings.pmus; } 192 | QList groupDescs() const { return m_groupDesc.groupDescs; } 193 | PerfCompressed compressed() const { return m_compressed; } 194 | 195 | private: 196 | void createFeature(QIODevice *device, QDataStream::ByteOrder byteOrder, 197 | PerfFileSection section, PerfHeader::Feature featureId); 198 | 199 | PerfTracingData m_tracingData; 200 | PerfBuildId m_buildId; 201 | PerfStringFeature m_hostName; 202 | PerfStringFeature m_osRelease; 203 | PerfStringFeature m_version; 204 | PerfStringFeature m_arch; 205 | PerfNrCpus m_nrCpus; 206 | PerfStringFeature m_cpuDesc; 207 | PerfStringFeature m_cpuId; 208 | PerfTotalMem m_totalMem; 209 | PerfCmdline m_cmdline; 210 | PerfEventDesc m_eventDesc; 211 | PerfCpuTopology m_cpuTopology; 212 | PerfNumaTopology m_numaToplogy; 213 | PerfPmuMappings m_pmuMappings; 214 | PerfGroupDesc m_groupDesc; 215 | PerfCompressed m_compressed; 216 | }; 217 | -------------------------------------------------------------------------------- /app/perffilesection.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perffilesection.h" 22 | 23 | QDataStream &operator>>(QDataStream &stream, PerfFileSection §ion) 24 | { 25 | return stream >> section.offset >> section.size; 26 | } 27 | 28 | 29 | PerfFileSection::PerfFileSection() : offset(0), size(0) {} 30 | -------------------------------------------------------------------------------- /app/perffilesection.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | struct PerfFileSection { 26 | PerfFileSection(); 27 | qint64 offset; 28 | qint64 size; 29 | 30 | static quint16 fixedLength() { return sizeof(offset) + sizeof(size); } 31 | }; 32 | 33 | QDataStream &operator>>(QDataStream &stream, PerfFileSection §ion); 34 | -------------------------------------------------------------------------------- /app/perfheader.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perfheader.h" 22 | 23 | #include 24 | 25 | PerfHeader::PerfHeader(QIODevice *source) : 26 | m_source(source), m_magic(0), m_size(0), m_attrSize(0) 27 | { 28 | connect(source, &QIODevice::readyRead, this, &PerfHeader::read); 29 | connect(source, &QIODevice::aboutToClose, this, &PerfHeader::error); 30 | for (uint i = 0; i < sizeof(m_features) / sizeof(quint64); ++i) 31 | m_features[i] = 0; 32 | } 33 | 34 | void PerfHeader::read() 35 | { 36 | const uint featureParts = sizeof(m_features) / sizeof(quint64); 37 | 38 | QDataStream stream(m_source); 39 | if (m_size == 0) { 40 | if (!m_source->isSequential() && m_source->size() < pipeHeaderFixedLength()) { 41 | qWarning() << "File is too small for perf header."; 42 | emit error(); 43 | return; 44 | } 45 | 46 | if (m_source->bytesAvailable() < pipeHeaderFixedLength()) 47 | return; 48 | 49 | stream >> m_magic; 50 | if (m_magic != s_magicSame && m_magic != s_magicSwitched) { 51 | qWarning() << "invalid magic:" << m_magic; 52 | qWarning() << "we don't support V1 perf data"; 53 | emit error(); 54 | return; 55 | } else { 56 | stream.setByteOrder(byteOrder()); 57 | } 58 | 59 | stream >> m_size; 60 | } 61 | 62 | if (m_size < pipeHeaderFixedLength()) { 63 | qWarning() << "Header claims to be smaller than magic + size:" << m_size; 64 | emit error(); 65 | return; 66 | } else if (m_size > pipeHeaderFixedLength()) { 67 | // read extended header information only available in perf.data files, 68 | // not in piped perf streams 69 | 70 | if (!m_source->isSequential() && m_source->size() < fileHeaderFixedLength()) { 71 | qWarning() << "File is too small for perf header."; 72 | emit error(); 73 | return; 74 | } 75 | 76 | if (m_source->bytesAvailable() < fileHeaderFixedLength() - pipeHeaderFixedLength()) 77 | return; 78 | 79 | // file header 80 | stream >> m_attrSize >> m_attrs >> m_data >> m_eventTypes; 81 | for (uint i = 0; i < featureParts; ++i) 82 | stream >> m_features[i]; 83 | 84 | if (m_magic == s_magicSwitched && !hasFeature(HOSTNAME) && !hasFeature(CMDLINE)) { 85 | 86 | quint32 *features32 = reinterpret_cast(&m_features[0]); 87 | for (uint i = 0; i < featureParts; ++i) 88 | qSwap(features32[i * 2], features32[i * 2 + 1]); 89 | 90 | if (!hasFeature(HOSTNAME) && !hasFeature(CMDLINE)) { 91 | // It borked: blank it all 92 | qWarning() << "bad feature data:" << m_features; 93 | for (uint i = 0; i < featureParts; ++i) 94 | m_features[i] = 0; 95 | setFeature(BUILD_ID); 96 | } 97 | } 98 | 99 | const auto minimumFileSize = static_cast(dataOffset() + dataSize()); 100 | if (!m_source->isSequential() && m_source->size() < minimumFileSize) { 101 | qWarning() << "File is too small to hold perf data."; 102 | emit error(); 103 | return; 104 | } 105 | 106 | if (m_size > fileHeaderFixedLength()) { 107 | if (m_size > std::numeric_limits::max()) { 108 | qWarning() << "Excessively large perf file header:" << m_size; 109 | emit error(); 110 | return; 111 | } 112 | qWarning() << "Header not completely read."; 113 | stream.skipRawData(static_cast(m_size) - fileHeaderFixedLength()); 114 | } 115 | } else { 116 | // pipe header, anything to do here? 117 | } 118 | 119 | disconnect(m_source, &QIODevice::readyRead, this, &PerfHeader::read); 120 | disconnect(m_source, &QIODevice::aboutToClose, this, &PerfHeader::error); 121 | m_source = nullptr; 122 | emit finished(); 123 | } 124 | 125 | quint16 PerfHeader::pipeHeaderFixedLength() 126 | { 127 | return sizeof(m_magic) + sizeof(m_size); 128 | } 129 | 130 | quint16 PerfHeader::fileHeaderFixedLength() 131 | { 132 | return pipeHeaderFixedLength() 133 | + sizeof(m_attrSize) 134 | + 3 * PerfFileSection::fixedLength() // m_attrs, m_data, m_eventTypes 135 | + sizeof(m_features); 136 | } 137 | 138 | QDataStream::ByteOrder PerfHeader::byteOrder() const 139 | { 140 | // magic is read in QDataStream's default big endian mode 141 | return m_magic == s_magicSame ? QDataStream::BigEndian : QDataStream::LittleEndian; 142 | } 143 | 144 | void PerfHeader::setFeature(PerfHeader::Feature feature) 145 | { 146 | Q_ASSERT(feature >= 0 && feature < sizeof(m_features) * sizeof(quint64)); 147 | m_features[feature / 64] |= (1ULL << (feature % 64)); 148 | } 149 | 150 | void PerfHeader::clearFeature(PerfHeader::Feature feature) 151 | { 152 | Q_ASSERT(feature >= 0 && feature < sizeof(m_features) * sizeof(quint64)); 153 | m_features[feature / 64] &= ~(1ULL << (feature % 64)); 154 | } 155 | 156 | bool PerfHeader::hasFeature(PerfHeader::Feature feature) const 157 | { 158 | Q_ASSERT(feature >= 0 && feature < sizeof(m_features) * sizeof(quint64)); 159 | return (m_features[feature / 64] & (1ULL << (feature % 64))) != 0; 160 | } 161 | 162 | 163 | -------------------------------------------------------------------------------- /app/perfheader.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include "perffilesection.h" 24 | 25 | #include 26 | #include 27 | 28 | class PerfHeader : public QObject { 29 | Q_OBJECT 30 | public: 31 | PerfHeader(QIODevice *source); 32 | 33 | enum Feature { 34 | RESERVED = 0, /* always cleared */ 35 | FIRST_FEATURE = 1, 36 | TRACING_DATA = 1, 37 | BUILD_ID, 38 | 39 | HOSTNAME, 40 | OSRELEASE, 41 | VERSION, 42 | ARCH, 43 | NRCPUS, 44 | CPUDESC, 45 | CPUID, 46 | TOTAL_MEM, 47 | CMDLINE, 48 | EVENT_DESC, 49 | CPU_TOPOLOGY, 50 | NUMA_TOPOLOGY, 51 | BRANCH_STACK, 52 | PMU_MAPPINGS, 53 | GROUP_DESC, 54 | AUXTRACE, 55 | STAT, 56 | CACHE, 57 | SAMPLE_TIME, 58 | MEM_TOPOLOGY, 59 | CLOCKID, 60 | DIR_FORMAT, 61 | BPF_PROG_INFO, 62 | BPF_BTF, 63 | COMPRESSED, 64 | CPU_PMU_CAPS, 65 | LAST_FEATURE, 66 | FEAT_BITS = 256, 67 | }; 68 | Q_ENUM(Feature) 69 | 70 | QDataStream::ByteOrder byteOrder() const; 71 | 72 | bool hasFeature(Feature feature) const; 73 | void setFeature(Feature feature); 74 | void clearFeature(Feature feature); 75 | 76 | qint64 numAttrs() const { return m_attrs.size > 0 ? m_attrs.size / m_attrSize : 0ll; } 77 | qint64 attrSize() const { return m_attrSize; } 78 | const PerfFileSection &attrs() const { return m_attrs; } 79 | 80 | qint64 featureOffset() const { return m_data.offset + m_data.size; } 81 | qint64 dataOffset() const { return m_data.offset; } 82 | qint64 dataSize() const { return m_data.size; } 83 | bool isPipe() const { return m_size == pipeHeaderFixedLength(); } 84 | 85 | qint64 size() const { return m_size; } 86 | 87 | public slots: 88 | void read(); 89 | 90 | signals: 91 | void finished(); 92 | void error(); 93 | 94 | private: 95 | static quint16 fileHeaderFixedLength(); 96 | static quint16 pipeHeaderFixedLength(); 97 | 98 | QIODevice *m_source; 99 | 100 | qint64 m_magic; 101 | qint64 m_size; 102 | qint64 m_attrSize; 103 | 104 | PerfFileSection m_attrs; 105 | PerfFileSection m_data; 106 | PerfFileSection m_eventTypes; 107 | 108 | quint64 m_features[FEAT_BITS / 64 + ((FEAT_BITS % 64) > 0 ? 1 : 0)]; 109 | 110 | static const qint64 s_magicSame = 0x32454c4946524550LL; 111 | static const qint64 s_magicSwitched = 0x50455246494c4532LL; 112 | }; 113 | -------------------------------------------------------------------------------- /app/perfkallsyms.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include "perfkallsyms.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | bool PerfKallsyms::parseMapping(const QString &path) 29 | { 30 | m_entries.clear(); 31 | m_errorString.clear(); 32 | 33 | QFile file(path); 34 | if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 35 | m_errorString = file.errorString(); 36 | return false; 37 | } 38 | 39 | QTextStream stream(&file); 40 | stream.setLocale(QLocale::c()); 41 | 42 | QByteArray address; 43 | 44 | // NOTE: don't use atEnd here, as /proc/kallsyms is has size of 0 45 | bool valid = true; 46 | while (true) { 47 | PerfKallsymEntry entry; 48 | char type = 0; 49 | char eol = '\n'; 50 | 51 | stream >> address >> Qt::ws >> type >> Qt::ws >> entry.symbol >> eol; 52 | if (address.isEmpty()) 53 | break; 54 | 55 | bool ok = false; 56 | entry.address = address.toULongLong(&ok, 16); 57 | if (!ok && address != "(null)") { 58 | m_errorString = tr("Invalid address: %1").arg(QString::fromUtf8(address)); 59 | valid = false; 60 | break; 61 | } 62 | 63 | if (eol == '\t') 64 | stream >> entry.module; 65 | 66 | if (entry.address != 0) 67 | m_entries.push_back(entry); 68 | } 69 | 70 | if (valid && m_entries.isEmpty()) { 71 | m_errorString = tr("Mapping is empty."); 72 | return false; 73 | } 74 | 75 | std::sort(m_entries.begin(), m_entries.end(), 76 | [](const PerfKallsymEntry& lhs, const PerfKallsymEntry& rhs) -> bool { 77 | return lhs.address < rhs.address; 78 | }); 79 | 80 | return valid; 81 | } 82 | 83 | PerfKallsymEntry PerfKallsyms::findEntry(quint64 address) const 84 | { 85 | auto entry = std::upper_bound(m_entries.begin(), m_entries.end(), address, 86 | [](quint64 address, const PerfKallsymEntry& entry) -> bool { 87 | return address < entry.address; 88 | }); 89 | 90 | if (entry != m_entries.begin()) { 91 | --entry; 92 | return *entry; 93 | } 94 | 95 | return {}; 96 | } 97 | -------------------------------------------------------------------------------- /app/perfkallsyms.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | struct PerfKallsymEntry 27 | { 28 | quint64 address = 0; 29 | QByteArray symbol; 30 | QByteArray module; 31 | }; 32 | 33 | QT_BEGIN_NAMESPACE 34 | Q_DECLARE_TYPEINFO(PerfKallsymEntry, Q_MOVABLE_TYPE); 35 | QT_END_NAMESPACE 36 | 37 | class PerfKallsyms 38 | { 39 | Q_DECLARE_TR_FUNCTIONS(PerfKallsyms) 40 | public: 41 | bool parseMapping(const QString &path); 42 | QString errorString() const { return m_errorString; } 43 | bool isEmpty() const { return m_entries.isEmpty(); } 44 | 45 | PerfKallsymEntry findEntry(quint64 address) const; 46 | 47 | private: 48 | QList m_entries; 49 | QString m_errorString; 50 | }; 51 | -------------------------------------------------------------------------------- /app/perfregisterinfo.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perfregisterinfo.h" 22 | 23 | #include 24 | #include 25 | 26 | const int PerfRegisterInfo::s_numRegisters[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = { 27 | {16, 16}, 28 | {33, 33}, 29 | { 0, 0}, 30 | { 0, 0}, 31 | { 0, 0}, 32 | { 0, 0}, 33 | { 9, 17}, 34 | }; 35 | 36 | const int PerfRegisterInfo::s_wordWidth[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = { 37 | {4, 4}, 38 | {8, 8}, 39 | {0, 0}, 40 | {0, 0}, 41 | {0, 0}, 42 | {0, 0}, 43 | {4, 8}, 44 | }; 45 | 46 | // Perf and Dwarf register layouts are the same for ARM and ARM64 47 | static int arm[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 48 | static int aarch64[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 49 | 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; 50 | 51 | // X86 is a mess 52 | static int x86[] = {0, 2, 3, 1, 7, 6, 4, 5, 8}; 53 | static int x86_64[] = {0, 3, 2, 1, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, 8}; 54 | 55 | #ifdef mips 56 | // On MIPS systems, "mips" is a built-in compiler macro. 57 | #undef mips 58 | #endif 59 | static int mips[] = { 32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 60 | 18, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31}; 61 | 62 | static int none[] = {0}; 63 | 64 | const int *PerfRegisterInfo::s_perfToDwarf[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = { 65 | {arm, arm }, 66 | {aarch64, aarch64}, 67 | {none, none }, 68 | {none, none }, 69 | {none, none }, 70 | {none, none }, 71 | {x86, x86_64 }, 72 | {mips, mips }, 73 | }; 74 | 75 | const int PerfRegisterInfo::s_perfIp[ARCH_INVALID] = { 76 | 15, 32, 0xffff, 0xffff, 0xffff, 0xffff, 8 77 | }; 78 | 79 | const int PerfRegisterInfo::s_perfSp[ARCH_INVALID] = { 80 | 13, 31, 0xffff, 0xffff, 0xffff, 0xffff, 7 81 | }; 82 | 83 | const int PerfRegisterInfo::s_dwarfLr[ARCH_INVALID][s_numAbis] = { 84 | {14, 14}, 85 | {30, 30}, 86 | {0xffff, 0xffff}, 87 | {0xffff, 0xffff}, 88 | {0xffff, 0xffff}, 89 | {0xffff, 0xffff}, 90 | {0xffff, 0xffff} 91 | }; 92 | 93 | const int PerfRegisterInfo::s_dwarfIp[ARCH_INVALID][s_numAbis] = { 94 | {15, 15}, 95 | {32, 32}, 96 | {0xffff, 0xffff}, 97 | {0xffff, 0xffff}, 98 | {0xffff, 0xffff}, 99 | {0xffff, 0xffff}, 100 | {8, 16} 101 | }; 102 | 103 | const int PerfRegisterInfo::s_dummyRegisters[ARCH_INVALID][2] = { 104 | {0, 0}, 105 | {72, 80}, 106 | {0, 0}, 107 | {0, 0}, 108 | {0, 0}, 109 | {0, 0}, 110 | {0, 0} 111 | }; 112 | 113 | QString PerfRegisterInfo::defaultArchitecture() 114 | { 115 | #if defined(__aarch64__) 116 | return QStringLiteral("aarch64"); 117 | #elif defined(__arm__) 118 | return QStringLiteral("arm"); 119 | #elif defined(__powerpc__) 120 | return QStringLiteral("powerpc"); 121 | #elif defined(__s390__) 122 | return QStringLiteral("s390"); 123 | #elif defined(__sh__) 124 | return QStringLiteral("sh"); 125 | #elif defined(__sparc__) 126 | return QStringLiteral("sparc"); 127 | #elif defined(__i386__) || defined(__x86_64__) 128 | return QStringLiteral("x86"); 129 | #else 130 | return QString(); 131 | #endif 132 | } 133 | 134 | PerfRegisterInfo::Architecture PerfRegisterInfo::archByName(const QByteArray &name) 135 | { 136 | if (name == "aarch64" || name == "arm64") 137 | return ARCH_AARCH64; 138 | 139 | if (name.startsWith("arm")) 140 | return ARCH_ARM; 141 | 142 | if (name.startsWith("powerpc")) 143 | return ARCH_POWERPC; 144 | 145 | if (name.startsWith("s390")) 146 | return ARCH_S390; 147 | 148 | if (name.startsWith("sh")) 149 | return ARCH_SH; 150 | 151 | if (name.startsWith("sparc")) 152 | return ARCH_SPARC; 153 | 154 | if (name.startsWith("x86") || name == "i386" || name == "i486" || name == "i586" || name == "i686" || name == "i786" 155 | || name == "amd64") 156 | return ARCH_X86; 157 | 158 | if (name.startsWith("mips")) 159 | return ARCH_MIPS; 160 | 161 | return ARCH_INVALID; 162 | } 163 | -------------------------------------------------------------------------------- /app/perfregisterinfo.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | class PerfRegisterInfo 26 | { 27 | public: 28 | enum Architecture { 29 | ARCH_ARM = 0, 30 | ARCH_AARCH64, 31 | ARCH_POWERPC, 32 | ARCH_S390, 33 | ARCH_SH, 34 | ARCH_SPARC, 35 | ARCH_X86, 36 | ARCH_MIPS, 37 | ARCH_INVALID 38 | }; 39 | 40 | static const int s_numAbis = 2; // maybe more for some archs? 41 | 42 | static Architecture archByName(const QByteArray &name); 43 | static const int s_numRegisters[ARCH_INVALID][s_numAbis]; 44 | static const int s_wordWidth[ARCH_INVALID][s_numAbis]; 45 | 46 | // Translation table for converting perf register layout to dwarf register layout 47 | // This is specific to ABI as the different ABIs may have different numbers of registers. 48 | static const int *s_perfToDwarf[ARCH_INVALID][s_numAbis]; 49 | 50 | // location of IP register or equivalent in perf register layout for each arch/abi 51 | // This is not specific to ABI as perf makes sure IP is always in the same spot 52 | static const int s_perfIp[ARCH_INVALID]; 53 | // location of SP register or equivalent in perf register layout for each arch/abi 54 | static const int s_perfSp[ARCH_INVALID]; 55 | 56 | // location of LR register or equivalent in dwarf register layout for each arch/abi 57 | static const int s_dwarfLr[ARCH_INVALID][s_numAbis]; 58 | // location of IP register or equivalent in dwarf register layout for each arch/abi 59 | static const int s_dwarfIp[ARCH_INVALID][s_numAbis]; 60 | 61 | // ranges of registers expected by libdw, but not provided by perf 62 | static const int s_dummyRegisters[ARCH_INVALID][2]; 63 | 64 | // default architecture for the system which was used for compilation 65 | static QString defaultArchitecture(); 66 | }; 67 | -------------------------------------------------------------------------------- /app/perfstdin.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perfstdin.h" 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | PerfStdin::PerfStdin(QObject *parent) : QIODevice(parent) 30 | { 31 | connect(&m_timer, &QTimer::timeout, this, &PerfStdin::receiveData); 32 | } 33 | 34 | PerfStdin::~PerfStdin() 35 | { 36 | if (isOpen()) 37 | close(); 38 | } 39 | 40 | bool PerfStdin::open(QIODevice::OpenMode mode) 41 | { 42 | if (!(mode & QIODevice::ReadOnly) || (mode & QIODevice::WriteOnly)) 43 | return false; 44 | 45 | if (QIODevice::open(mode)) { 46 | m_buffer.resize(s_minBufferSize); 47 | m_timer.start(); 48 | return true; 49 | } else { 50 | return false; 51 | } 52 | } 53 | 54 | void PerfStdin::close() 55 | { 56 | m_timer.stop(); 57 | QIODevice::close(); 58 | } 59 | 60 | qint64 PerfStdin::readData(char *data, qint64 maxlen) 61 | { 62 | if (maxlen <= 0) 63 | return 0; 64 | 65 | qint64 read = 0; 66 | do { 67 | Q_ASSERT(m_buffer.length() >= m_bufferPos); 68 | Q_ASSERT(m_buffer.length() >= m_bufferUsed); 69 | Q_ASSERT(m_bufferPos <= m_bufferUsed); 70 | const size_t buffered = static_cast(qMin(bufferedAvailable(), maxlen - read)); 71 | memcpy(data + read, m_buffer.constData() + m_bufferPos, buffered); 72 | m_bufferPos += buffered; 73 | read += buffered; 74 | } while (read < maxlen && fillBuffer(maxlen) > 0); 75 | 76 | Q_ASSERT(read > 0 || bufferedAvailable() == 0); 77 | return (read == 0 && stdinAtEnd()) ? -1 : read; 78 | } 79 | 80 | qint64 PerfStdin::writeData(const char *data, qint64 len) 81 | { 82 | Q_UNUSED(data); 83 | Q_UNUSED(len); 84 | return -1; 85 | } 86 | 87 | void PerfStdin::receiveData() 88 | { 89 | if (fillBuffer() > 0) 90 | emit readyRead(); 91 | else if (stdinAtEnd()) 92 | close(); 93 | } 94 | 95 | void PerfStdin::resizeBuffer(int newSize) 96 | { 97 | QByteArray newBuffer(newSize, Qt::Uninitialized); 98 | std::memcpy(newBuffer.data(), m_buffer.data() + m_bufferPos, 99 | static_cast(m_bufferUsed - m_bufferPos)); 100 | qSwap(m_buffer, newBuffer); 101 | m_bufferUsed -= m_bufferPos; 102 | Q_ASSERT(m_buffer.length() >= m_bufferUsed); 103 | m_bufferPos = 0; 104 | } 105 | 106 | qint64 PerfStdin::fillBuffer(qint64 targetBufferSize) 107 | { 108 | if (m_bufferUsed == m_bufferPos) 109 | m_bufferPos = m_bufferUsed = 0; 110 | 111 | targetBufferSize = qMin(targetBufferSize, static_cast(s_maxBufferSize)); 112 | if (targetBufferSize > m_buffer.length()) 113 | resizeBuffer(static_cast(targetBufferSize)); 114 | 115 | if (m_bufferUsed == m_buffer.length()) { 116 | if (m_bufferPos == 0) { 117 | resizeBuffer(m_bufferUsed <= s_maxBufferSize / 2 ? m_bufferUsed * 2 118 | : s_maxBufferSize); 119 | } else { 120 | resizeBuffer(m_bufferUsed); 121 | } 122 | } 123 | 124 | const size_t read = fread(m_buffer.data() + m_bufferUsed, 1, 125 | static_cast(m_buffer.length() - m_bufferUsed), stdin); 126 | m_bufferUsed += read; 127 | Q_ASSERT(m_buffer.length() >= m_bufferUsed); 128 | return static_cast(read); 129 | } 130 | 131 | bool PerfStdin::stdinAtEnd() const 132 | { 133 | return feof(stdin) || ferror(stdin); 134 | } 135 | 136 | bool PerfStdin::isSequential() const 137 | { 138 | return true; 139 | } 140 | 141 | qint64 PerfStdin::bytesAvailable() const 142 | { 143 | return bufferedAvailable() + QIODevice::bytesAvailable(); 144 | } 145 | -------------------------------------------------------------------------------- /app/perfstdin.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | class PerfStdin : public QIODevice 27 | { 28 | Q_OBJECT 29 | public: 30 | PerfStdin(QObject *parent = nullptr); 31 | ~PerfStdin(); 32 | 33 | bool open(OpenMode mode) override; 34 | void close() override; 35 | bool isSequential() const override; 36 | qint64 bytesAvailable() const override; 37 | 38 | protected: 39 | qint64 readData(char *data, qint64 maxlen) override; 40 | qint64 writeData(const char *data, qint64 len) override; 41 | 42 | private: 43 | static const int s_minBufferSize = 1 << 10; 44 | static const int s_maxBufferSize = 1 << 30; 45 | 46 | void receiveData(); 47 | void resizeBuffer(int newSize); 48 | qint64 fillBuffer(qint64 targetSize = -1); 49 | qint64 bufferedAvailable() const { return m_bufferUsed - m_bufferPos; } 50 | bool stdinAtEnd() const; 51 | 52 | QTimer m_timer; 53 | QByteArray m_buffer; 54 | int m_bufferPos = 0; 55 | int m_bufferUsed = 0; 56 | }; 57 | -------------------------------------------------------------------------------- /app/perfsymboltable.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of Qt Creator. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #pragma once 27 | 28 | #include "perfaddresscache.h" 29 | #include "perfdata.h" 30 | #include "perfelfmap.h" 31 | #include "perfunwind.h" 32 | 33 | #include 34 | 35 | #include 36 | 37 | class PerfDwarfDieCache; 38 | class SubProgramDie; 39 | class CuDieRangeMapping; 40 | 41 | class PerfSymbolTable 42 | { 43 | public: 44 | PerfSymbolTable(qint32 pid, Dwfl_Callbacks *callbacks, PerfUnwind *parent); 45 | ~PerfSymbolTable(); 46 | 47 | static QFileInfo findDebugInfoFile( 48 | const QString& root, const QString& file, const QString& debugLinkString); 49 | 50 | struct PerfMapSymbol { 51 | PerfMapSymbol(quint64 start = 0, quint64 length = 0, const QByteArray &name = QByteArray()) : 52 | start(start), length(length), name(name) {} 53 | quint64 start; 54 | quint64 length; 55 | QByteArray name; 56 | }; 57 | 58 | // Announce an mmap. Invalidate the symbol and address cache and clear the dwfl if it overlaps 59 | // with an existing one. 60 | void registerElf(const PerfRecordMmap &mmap, const QByteArray &buildId); 61 | 62 | PerfElfMap::ElfInfo findElf(quint64 ip) const; 63 | 64 | // Find the module for the given address and report it if needed 65 | Dwfl_Module *module(quint64 addr); 66 | Dwfl_Module *module(quint64 addr, const PerfElfMap::ElfInfo &elf); 67 | int findDebugInfo(Dwfl_Module *module, const char *moduleName, Dwarf_Addr base, 68 | const char *file, const char *debugLink, 69 | GElf_Word crc, char **debugInfoFilename); 70 | 71 | // Look up a frame and all its inline parents and append them to the given vector. 72 | // If the frame hits an elf that hasn't been reported, yet, report it. 73 | int lookupFrame(Dwarf_Addr ip, bool isKernel, bool *isInterworking); 74 | 75 | void updatePerfMap(); 76 | bool containsAddress(quint64 address) const; 77 | 78 | Dwfl *attachDwfl(const Dwfl_Thread_Callbacks *callbacks, PerfUnwind::UnwindInfo *unwindInfo); 79 | void clearCache(); 80 | bool cacheIsDirty() const { return m_cacheIsDirty; } 81 | 82 | void initAfterFork(const PerfSymbolTable *parent); 83 | 84 | private: 85 | // Report an mmap to dwfl and parse it for symbols and inlines, or simply return it if dwfl has 86 | // it already 87 | Dwfl_Module *reportElf(const PerfElfMap::ElfInfo& elf); 88 | QFileInfo findFile(const QString& path, const QString& fileName, const QByteArray& buildId = QByteArray()) const; 89 | 90 | class ElfAndFile { 91 | public: 92 | ElfAndFile() {} 93 | explicit ElfAndFile(const QFileInfo &fullPath); 94 | ElfAndFile(ElfAndFile &&other); 95 | ElfAndFile &operator=(ElfAndFile &&other); 96 | ElfAndFile(const ElfAndFile &other) = delete; 97 | ElfAndFile &operator=(const ElfAndFile &other) = delete; 98 | ~ElfAndFile(); 99 | 100 | Elf *elf() const { return m_elf; } 101 | QFileInfo fullPath() const { return m_fullPath; } 102 | 103 | private: 104 | void clear(); 105 | 106 | Elf *m_elf = nullptr; 107 | int m_file = -1; 108 | QFileInfo m_fullPath; 109 | }; 110 | 111 | QFile m_perfMapFile; 112 | QList m_perfMap; 113 | bool m_hasPerfMap; 114 | bool m_cacheIsDirty; 115 | 116 | PerfUnwind *m_unwind; 117 | Dwfl *m_dwfl; 118 | // elf used to detect architecture 119 | ElfAndFile m_firstElf; 120 | 121 | PerfElfMap m_elfs; 122 | PerfAddressCache::OffsetAddressCache m_invalidAddressCache; 123 | QHash m_cuDieRanges; 124 | Dwfl_Callbacks *m_callbacks; 125 | qint32 m_pid; 126 | 127 | QByteArray symbolFromPerfMap(quint64 ip, GElf_Off *offset) const; 128 | int parseDie(CuDieRangeMapping *cudie, Dwarf_Die *top, quint64 offset, quint64 size, quint64 relAddr, qint32 binaryId, qint32 binaryPathId, qint32 actualPathId, bool isKernel, 129 | Dwarf_Files *files, Dwarf_Addr entry, qint32 parentLocationId); 130 | int insertSubprogram(CuDieRangeMapping *cudie, Dwarf_Die *top, Dwarf_Addr entry, quint64 offset, quint64 size, quint64 relAddr, qint32 binaryId, qint32 binaryPathId, qint32 actualPathId, 131 | qint32 inlineParent, bool isKernel); 132 | qint32 parseDwarf(CuDieRangeMapping *cudie, SubProgramDie *subprogram, const QList &inlined, 133 | Dwarf_Addr bias, quint64 offset, quint64 size, quint64 relAddr, qint32 binaryId, qint32 binaryPathId, qint32 actualPathId, bool isKernel); 134 | }; 135 | 136 | QT_BEGIN_NAMESPACE 137 | Q_DECLARE_TYPEINFO(PerfSymbolTable::PerfMapSymbol, Q_MOVABLE_TYPE); 138 | QT_END_NAMESPACE 139 | -------------------------------------------------------------------------------- /app/perftracingdata.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perfattributes.h" 22 | #include "perftracingdata.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | static QByteArray readNullTerminatedString(QDataStream &stream) 31 | { 32 | QByteArray string; 33 | qint8 read = 0; 34 | while (true) { 35 | stream >> read; 36 | if (read != 0) 37 | string.append(read); 38 | else 39 | return string; 40 | } 41 | } 42 | 43 | static bool checkMagic(QDataStream &stream, const QByteArray &magic) 44 | { 45 | QByteArray read(magic.size(), Qt::Uninitialized); 46 | stream.readRawData(read.data(), read.size()); 47 | if (read != magic) { 48 | qWarning() << "Invalid magic in perf tracing data" << read << " - expected" << magic; 49 | return false; 50 | } 51 | return true; 52 | } 53 | 54 | template 55 | static bool checkSize(Number size) 56 | { 57 | if (sizeof(Number) >= sizeof(int) && size > Number(std::numeric_limits::max())) { 58 | qWarning() << "Excessively large section in tracing data" << size; 59 | return false; 60 | } 61 | return true; 62 | } 63 | 64 | const EventFormat &PerfTracingData::eventFormat(qint32 id) const 65 | { 66 | static EventFormat invalid; 67 | auto it = m_eventFormats.constFind(id); 68 | if (it != m_eventFormats.constEnd()) 69 | return *it; 70 | else 71 | return invalid; 72 | } 73 | 74 | bool PerfTracingData::readHeaderFiles(QDataStream &stream) 75 | { 76 | if (!checkMagic(stream, QByteArray("header_page") + '\0')) 77 | return false; 78 | 79 | quint64 size; 80 | stream >> size; 81 | 82 | if (!checkSize(size)) 83 | return false; 84 | 85 | QByteArray buffer(static_cast(size), Qt::Uninitialized); 86 | stream.readRawData(buffer.data(), buffer.size()); 87 | 88 | const auto lines = buffer.split('\n'); 89 | for (const auto &line : lines) { 90 | if (!line.isEmpty()) 91 | m_headerFields << readFormatField(line); 92 | } 93 | 94 | if (!checkMagic(stream, QByteArray("header_event") + '\0')) 95 | return false; 96 | 97 | stream >> size; 98 | if (!checkSize(size)) 99 | return false; 100 | 101 | stream.skipRawData(static_cast(size)); 102 | 103 | return true; 104 | } 105 | 106 | static void processLine(const QByteArray &line, 107 | const std::function &handler) 108 | { 109 | const auto chunks = line.split('\t'); 110 | for (const auto &chunk : chunks) { 111 | QList segments = chunk.split(':'); 112 | if (segments.size() != 2) 113 | continue; 114 | 115 | QByteArray name = segments[0].toLower(); 116 | QByteArray value = segments[1].trimmed(); 117 | if (value.endsWith(';')) 118 | value.chop(1); 119 | handler(name, value); 120 | } 121 | } 122 | 123 | FormatField PerfTracingData::readFormatField(const QByteArray &line) 124 | { 125 | FormatField field; 126 | processLine(line, [&](const QByteArray &name, const QByteArray &value) { 127 | if (name == "field") { 128 | QList fieldSegments = value.trimmed().split(' '); 129 | QByteArray fieldName = fieldSegments.length() > 0 ? fieldSegments.takeLast() 130 | : QByteArray(); 131 | if (fieldName.startsWith('*')) { 132 | field.flags |= FIELD_IS_POINTER; 133 | fieldName.remove(0, 1); 134 | } 135 | if (fieldName.endsWith(']')) { 136 | const int opening = fieldName.lastIndexOf('['); 137 | if (opening >= 0) { 138 | field.flags |= FIELD_IS_ARRAY; 139 | field.arraylen = fieldName.mid(opening + 1, 140 | fieldName.length() - opening - 2).toUInt(); 141 | fieldName.chop(fieldName.length() - opening); 142 | } 143 | } 144 | 145 | field.name = fieldName; 146 | if (fieldSegments.length() > 0 && fieldSegments.last() == "[]") { 147 | fieldSegments.removeLast(); 148 | field.flags |= FIELD_IS_ARRAY; 149 | } 150 | field.type = fieldSegments.join(' '); 151 | } else if (name == "offset") { 152 | field.offset = value.toUInt(); 153 | } else if (name == "size") { 154 | field.size = value.toUInt(); 155 | } else if (name == "signed") { 156 | if (value.toInt() != 0) 157 | field.flags |= FIELD_IS_SIGNED; 158 | } 159 | }); 160 | 161 | if (field.type.startsWith("__data_loc")) 162 | field.flags |= FIELD_IS_DYNAMIC; 163 | if (field.type.contains("long")) 164 | field.flags |= FIELD_IS_LONG; 165 | 166 | if (field.flags & FIELD_IS_ARRAY) { 167 | if (field.type.contains("char") || field.type.contains("u8") || field.type.contains("s8")) { 168 | field.flags |= FIELD_IS_STRING; 169 | field.elementsize = 1; 170 | } else if (field.arraylen > 0) { 171 | field.elementsize = field.size / field.arraylen; 172 | } else if (field.type.contains("u16") || field.type.contains("s16")) { 173 | field.elementsize = 2; 174 | } else if (field.type.contains("u32") || field.type.contains("s32")) { 175 | field.elementsize = 4; 176 | } else if (field.type.contains("u64") || field.type.contains("s64")) { 177 | field.elementsize = 8; 178 | } else if (field.flags & FIELD_IS_LONG) { 179 | field.elementsize = m_fileLongSize; 180 | } 181 | } else { 182 | field.elementsize = field.size; 183 | } 184 | return field; 185 | } 186 | 187 | enum FieldStage { 188 | BeforeFields, 189 | CommonFields, 190 | NonCommonFields, 191 | AfterFields 192 | }; 193 | 194 | bool PerfTracingData::readEventFormats(QDataStream &stream, const QByteArray &system) 195 | { 196 | qint32 count; 197 | stream >> count; 198 | 199 | for (qint32 x = 0; x < count; ++x) { 200 | qint32 id = -1; 201 | bool seenId = false; 202 | EventFormat event; 203 | quint64 size; 204 | stream >> size; 205 | if (!checkSize(size)) 206 | return false; 207 | 208 | event.system = system; 209 | if (system == "ftrace") 210 | event.flags |= EVENT_FL_ISFTRACE; 211 | 212 | QByteArray buffer(static_cast(size), Qt::Uninitialized); 213 | stream.readRawData(buffer.data(), buffer.length()); 214 | 215 | FieldStage stage = BeforeFields; 216 | const auto lines = buffer.split('\n'); 217 | for (const auto &line : lines) { 218 | switch (stage) { 219 | case CommonFields: 220 | if (line.isEmpty()) 221 | stage = NonCommonFields; 222 | else 223 | event.commonFields.append(readFormatField(line)); 224 | break; 225 | case NonCommonFields: 226 | if (line.isEmpty()) 227 | stage = AfterFields; 228 | else 229 | event.fields.append(readFormatField(line)); 230 | break; 231 | case BeforeFields: 232 | case AfterFields: 233 | processLine(line, [&](const QByteArray &name, const QByteArray &value) { 234 | if (name == "name") { 235 | event.name = value; 236 | if ((event.flags & EVENT_FL_ISFTRACE) && value == "bprint") 237 | event.flags |= EVENT_FL_ISBPRINT; 238 | } else if (name == "id") { 239 | id = value.toInt(); 240 | seenId = true; 241 | } else if (name == "format") { 242 | stage = CommonFields; 243 | } 244 | }); 245 | } 246 | } 247 | 248 | if (!seenId) { 249 | qWarning() << "No ID seen in event format"; 250 | return false; 251 | } 252 | 253 | m_eventFormats[id] = event; 254 | } 255 | 256 | return true; 257 | } 258 | 259 | bool PerfTracingData::readEventFiles(QDataStream &stream) 260 | { 261 | qint32 systems; 262 | stream >> systems; 263 | for (qint32 i = 0; i < systems; ++i) { 264 | if (!readEventFormats(stream, readNullTerminatedString(stream))) 265 | return false; 266 | } 267 | 268 | return true; 269 | } 270 | 271 | bool PerfTracingData::readProcKallsyms(QDataStream &stream) 272 | { 273 | quint32 size; 274 | stream >> size; 275 | if (!checkSize(size)) 276 | return false; 277 | stream.skipRawData(static_cast(size)); // unused, also in perf 278 | return true; 279 | } 280 | 281 | bool PerfTracingData::readFtracePrintk(QDataStream &stream) 282 | { 283 | quint32 size; 284 | stream >> size; 285 | 286 | if (!checkSize(size)) 287 | return false; 288 | 289 | QByteArray buffer(static_cast(size), Qt::Uninitialized); 290 | stream.readRawData(buffer.data(), buffer.length()); 291 | 292 | const auto lines = buffer.split('\n'); 293 | for (const auto &line : lines) { 294 | if (!line.isEmpty()) { 295 | QList segments = line.split(':'); 296 | if (segments.length() == 2) { 297 | QByteArray value = segments[1].trimmed(); 298 | m_ftracePrintk[segments[0].trimmed().toULongLong(nullptr, 0)] 299 | = value.mid(1, value.length() - 2); 300 | } 301 | } 302 | } 303 | 304 | return true; 305 | } 306 | 307 | bool PerfTracingData::readSavedCmdline(QDataStream &stream) 308 | { 309 | quint64 size; 310 | stream >> size; 311 | if (!checkSize(size)) 312 | return false; 313 | 314 | QByteArray buffer(static_cast(size), Qt::Uninitialized); 315 | stream.readRawData(buffer.data(), buffer.length()); 316 | 317 | const auto lines = buffer.split('\n'); 318 | for (const auto &line : lines) { 319 | // Each line is prefixed with the PID it refers to 320 | if (!line.isEmpty()) 321 | m_savedCmdlines.append(line); 322 | } 323 | 324 | return true; 325 | } 326 | 327 | QDataStream &operator>>(QDataStream &parentStream, PerfTracingData &record) 328 | { 329 | if (!checkSize(record.m_size)) { 330 | parentStream.skipRawData(std::numeric_limits::max()); 331 | parentStream.skipRawData(static_cast(record.m_size - std::numeric_limits::max())); 332 | return parentStream; 333 | } 334 | 335 | QByteArray data(static_cast(record.m_size), Qt::Uninitialized); 336 | parentStream.readRawData(data.data(), data.size()); 337 | QDataStream stream(data); 338 | 339 | if (!checkMagic(stream, "\027\bDtracing")) 340 | return parentStream; 341 | 342 | record.m_version = readNullTerminatedString(stream); 343 | 344 | qint8 read; 345 | stream >> read; 346 | record.m_bigEndian = (read != 0); 347 | stream.setByteOrder(record.m_bigEndian ? QDataStream::BigEndian : QDataStream::LittleEndian); 348 | stream >> read; 349 | record.m_fileLongSize = (read != 0); 350 | stream >> record.m_filePageSize; 351 | 352 | if (!record.readHeaderFiles(stream)) 353 | return parentStream; 354 | if (!record.readEventFormats(stream, "ftrace")) 355 | return parentStream; 356 | if (!record.readEventFiles(stream)) 357 | return parentStream; 358 | if (!record.readProcKallsyms(stream)) 359 | return parentStream; 360 | if (!record.readFtracePrintk(stream)) 361 | return parentStream; 362 | if (record.m_version.toFloat() >= 0.6f) { 363 | if (!record.readSavedCmdline(stream)) 364 | return parentStream; 365 | } 366 | 367 | const qint64 padding = record.m_size - stream.device()->pos(); 368 | if (padding >= 8) 369 | qWarning() << "More trace data left after parsing:" << padding; 370 | 371 | return parentStream; 372 | } 373 | -------------------------------------------------------------------------------- /app/perftracingdata.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd 4 | ** All rights reserved. 5 | ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | enum FormatFlags: quint32 28 | { 29 | FIELD_IS_ARRAY = 1 << 0, 30 | FIELD_IS_POINTER = 1 << 1, 31 | FIELD_IS_SIGNED = 1 << 2, 32 | FIELD_IS_STRING = 1 << 3, 33 | FIELD_IS_DYNAMIC = 1 << 4, 34 | FIELD_IS_LONG = 1 << 5, 35 | FIELD_IS_FLAG = 1 << 6, 36 | FIELD_IS_SYMBOLIC = 1 << 7, 37 | }; 38 | 39 | struct FormatField 40 | { 41 | QByteArray type; 42 | QByteArray name; 43 | quint32 offset = 0; 44 | quint32 size = 0; 45 | quint32 arraylen = 0; 46 | quint32 elementsize = 0; 47 | quint32 flags = 0; 48 | }; 49 | 50 | enum EventFormatFlags { 51 | EVENT_FL_ISFTRACE = 0x01, 52 | EVENT_FL_ISPRINT = 0x02, 53 | EVENT_FL_ISBPRINT = 0x04, 54 | EVENT_FL_ISFUNCENT = 0x10, 55 | EVENT_FL_ISFUNCRET = 0x20, 56 | EVENT_FL_NOHANDLE = 0x40, 57 | EVENT_FL_PRINTRAW = 0x80, 58 | 59 | EVENT_FL_FAILED = 0x80000000 60 | }; 61 | 62 | struct EventFormat 63 | { 64 | QByteArray name; 65 | QByteArray system; 66 | QList commonFields; 67 | QList fields; 68 | quint32 flags = 0; 69 | }; 70 | 71 | class PerfTracingData 72 | { 73 | public: 74 | quint32 size() const { return m_size; } 75 | void setSize(quint32 size) { m_size = size; } 76 | QByteArray version() const { return m_version; } 77 | const EventFormat &eventFormat(qint32 id) const; 78 | const QHash &eventFormats() const {return m_eventFormats; } 79 | 80 | private: 81 | bool readHeaderFiles(QDataStream &stream); 82 | bool readFtraceFiles(QDataStream &stream); 83 | bool readEventFiles(QDataStream &stream); 84 | bool readProcKallsyms(QDataStream &stream); 85 | bool readFtracePrintk(QDataStream &stream); 86 | bool readSavedCmdline(QDataStream &stream); 87 | bool readEventFormats(QDataStream &stream, const QByteArray &system); 88 | 89 | FormatField readFormatField(const QByteArray &line); 90 | 91 | quint32 m_size = 0; 92 | QByteArray m_version; 93 | bool m_bigEndian = false; 94 | bool m_fileLongSize = false; 95 | qint32 m_filePageSize = false; 96 | 97 | QHash m_eventFormats; 98 | QList m_headerFields; 99 | QHash m_ftracePrintk; 100 | QList m_savedCmdlines; 101 | 102 | friend QDataStream &operator>>(QDataStream &stream, PerfTracingData &record); 103 | }; 104 | 105 | QDataStream &operator>>(QDataStream &stream, PerfTracingData &record); 106 | -------------------------------------------------------------------------------- /cmake/FindLibDDemangle.cmake: -------------------------------------------------------------------------------- 1 | if (LIBD_DEMANGLE_LIBRARIES) 2 | set (LibDDemangle_FIND_QUIETLY TRUE) 3 | endif() 4 | 5 | find_library(LIBD_DEMANGLE_LIBRARIES 6 | NAMES 7 | d_demangle 8 | PATHS 9 | /usr/lib 10 | /usr/local/lib 11 | /opt/local/lib 12 | /sw/lib 13 | ENV LIBRARY_PATH 14 | ENV LD_LIBRARY_PATH) 15 | 16 | include (FindPackageHandleStandardArgs) 17 | 18 | # handle the QUIETLY and REQUIRED arguments and set LIBRUSTC_DEMANGLE_FOUND to TRUE if all listed variables are TRUE 19 | find_package_handle_standard_args(LibDDemangle DEFAULT_MSG 20 | LIBD_DEMANGLE_LIBRARIES) 21 | 22 | mark_as_advanced(LIBD_DEMANGLE_LIBRARIES) 23 | -------------------------------------------------------------------------------- /cmake/FindLibRustcDemangle.cmake: -------------------------------------------------------------------------------- 1 | if (LIBRUSTC_DEMANGLE_LIBRARIES) 2 | set (LibRustcDemangle_FIND_QUIETLY TRUE) 3 | endif() 4 | 5 | find_library(LIBRUSTC_DEMANGLE_LIBRARIES 6 | NAMES 7 | rustc_demangle 8 | PATHS 9 | /usr/lib 10 | /usr/local/lib 11 | /opt/local/lib 12 | /sw/lib 13 | ENV LIBRARY_PATH 14 | ENV LD_LIBRARY_PATH) 15 | 16 | include (FindPackageHandleStandardArgs) 17 | 18 | # handle the QUIETLY and REQUIRED arguments and set LIBRUSTC_DEMANGLE_FOUND to TRUE if all listed variables are TRUE 19 | find_package_handle_standard_args(LibRustcDemangle DEFAULT_MSG 20 | LIBRUSTC_DEMANGLE_LIBRARIES) 21 | 22 | mark_as_advanced(LIBRUSTC_DEMANGLE_LIBRARIES) 23 | -------------------------------------------------------------------------------- /cmake/FindZstd.cmake: -------------------------------------------------------------------------------- 1 | # taken from: https://github.com/facebook/folly/blob/master/CMake/FindZstd.cmake 2 | # should be apache 2.0, cf.: https://github.com/facebook/folly/blob/master/LICENSE 3 | # 4 | # - Try to find Facebook zstd library 5 | # This will define 6 | # Zstd_FOUND 7 | # Zstd_INCLUDE_DIR 8 | # Zstd_LIBRARY 9 | # 10 | 11 | find_path(Zstd_INCLUDE_DIR NAMES zstd.h) 12 | find_library(Zstd_LIBRARY NAMES zstd) 13 | 14 | include(FindPackageHandleStandardArgs) 15 | FIND_PACKAGE_HANDLE_STANDARD_ARGS( 16 | Zstd DEFAULT_MSG 17 | Zstd_LIBRARY Zstd_INCLUDE_DIR 18 | ) 19 | 20 | mark_as_advanced(Zstd_INCLUDE_DIR Zstd_LIBRARY) 21 | -------------------------------------------------------------------------------- /elfutils.pri: -------------------------------------------------------------------------------- 1 | !isEmpty(ELFUTILS_INSTALL_DIR) { 2 | INCLUDEPATH += $$ELFUTILS_INSTALL_DIR/include $$ELFUTILS_INSTALL_DIR/include/elfutils 3 | LIBS += -L$$ELFUTILS_INSTALL_DIR/lib 4 | } else:unix { 5 | INCLUDEPATH += /usr/include/elfutils 6 | } 7 | 8 | LIBS += -ldw -lelf 9 | 10 | win32 { 11 | LIBS += -leu_compat 12 | } 13 | 14 | linux:!isEmpty(PERFPARSER_ELFUTILS_INSTALLDIR) { 15 | RPATH = $$relative_path($$PERFPARSER_ELFUTILS_INSTALLDIR, $$PERFPARSER_APP_INSTALLDIR) 16 | QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,\$\$ORIGIN/$$RPATH\' 17 | } 18 | -------------------------------------------------------------------------------- /paths.pri: -------------------------------------------------------------------------------- 1 | 2 | isEmpty(PERFPARSER_APP_DESTDIR): PERFPARSER_APP_DESTDIR = $$shadowed($$PWD)/bin 3 | isEmpty(PERFPARSER_INSTALLDIR_PREFIX): PERFPARSER_INSTALLDIR_PREFIX = /usr/local 4 | isEmpty(PERFPARSER_APP_INSTALLDIR): PERFPARSER_APP_INSTALLDIR = $$PERFPARSER_INSTALLDIR_PREFIX/bin 5 | 6 | isEmpty(PERFPARSER_ELFUTILS_BACKENDS_INSTALLDIR):!isEmpty(PERFPARSER_ELFUTILS_INSTALLDIR) { 7 | PERFPARSER_ELFUTILS_BACKENDS_INSTALLDIR = $$PERFPARSER_ELFUTILS_INSTALLDIR/elfutils 8 | } 9 | -------------------------------------------------------------------------------- /perfparser.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | isEmpty(ELFUTILS_INSTALL_DIR) { 4 | unix { 5 | ELFUTILS_INCLUDE_DIR = /usr/include 6 | } else { 7 | warning("Cannot automatically infer the elfutils include and lib directories.") 8 | } 9 | } else { 10 | ELFUTILS_INCLUDE_DIR = $$ELFUTILS_INSTALL_DIR/include 11 | } 12 | 13 | exists($$ELFUTILS_INCLUDE_DIR/libdwfl.h)|exists($$ELFUTILS_INCLUDE_DIR/elfutils/libdwfl.h) { 14 | SUBDIRS = app 15 | !isEmpty(BUILD_TESTS): SUBDIRS += tests 16 | 17 | include (paths.pri) 18 | 19 | defineReplace(elfutilsLibraryName) { 20 | RET = $$1 21 | linux: RET = lib$${RET}.so.$$2 22 | macos: RET = lib$${RET}.dylib 23 | win32: RET = $${RET}.dll 24 | return($$RET) 25 | } 26 | 27 | !isEmpty(PERFPARSER_ELFUTILS_INSTALLDIR) { 28 | ELFUTILS_LIB_DIR = $$ELFUTILS_INSTALL_DIR/lib 29 | inst_elfutils.files = \ 30 | $$ELFUTILS_LIB_DIR/$$elfutilsLibraryName(elf, 1) \ 31 | $$ELFUTILS_LIB_DIR/$$elfutilsLibraryName(dw, 1) 32 | 33 | win32: inst_elfutils.files += $$ELFUTILS_LIB_DIR/eu_compat.dll 34 | 35 | inst_elfutils.path = $$PERFPARSER_ELFUTILS_INSTALLDIR 36 | inst_elfutils.CONFIG += no_check_exist no_default_install 37 | 38 | # only deploy the non-versioned backends. We are never dlopen'ing the versioned ones anyway. 39 | inst_backends.files = $$files($$ELFUTILS_LIB_DIR/elfutils/*ebl_*.*) 40 | inst_backends.files -= $$files($$ELFUTILS_LIB_DIR/elfutils/*ebl_*-*.*.*) 41 | inst_backends.path = $$PERFPARSER_ELFUTILS_BACKENDS_INSTALLDIR 42 | inst_backends.CONFIG += no_check_exist no_default_install 43 | 44 | INSTALLS += inst_backends inst_elfutils 45 | 46 | deployqt.depends = install_inst_elfutils install_inst_backends 47 | 48 | linux { 49 | RPATH = $$relative_path($$PERFPARSER_ELFUTILS_BACKENDS_INSTALLDIR, \ 50 | $$PERFPARSER_ELFUTILS_INSTALLDIR) 51 | fix_dw_rpath.commands = chrpath -r \'\$\$ORIGIN/$$RPATH\' \ 52 | $$PERFPARSER_ELFUTILS_INSTALLDIR/$$elfutilsLibraryName(dw, 1) 53 | fix_dw_rpath.depends = install_inst_elfutils 54 | deployqt.depends += fix_dw_rpath 55 | QMAKE_EXTRA_TARGETS += fix_dw_rpath install_inst_elfutils 56 | } 57 | } 58 | } else { 59 | warning("PerfParser is disabled. Set ELFUTILS_INSTALL_DIR to enable it."); 60 | } 61 | 62 | OTHER_FILES += perfparser.qbs 63 | 64 | QMAKE_EXTRA_TARGETS += deployqt docs install_docs 65 | -------------------------------------------------------------------------------- /perfparser.qbs: -------------------------------------------------------------------------------- 1 | import qbs.Environment 2 | import qbs.FileInfo 3 | 4 | Project { 5 | name: "Perf Parser" 6 | condition: qbs.targetOS.contains("linux") 7 | 8 | property bool withAutotests: qbs.buildVariant === "debug" 9 | 10 | property string installBase: Environment.getEnv("ELFUTILS_INSTALL_DIR") 11 | property stringList includePaths: installBase 12 | ? [FileInfo.joinPaths(installBase, "include"), 13 | FileInfo.joinPaths(installBase, "include", "elfutils")] 14 | : "/usr/include/elfutils" 15 | property stringList libPaths: installBase ? [FileInfo.joinPaths(installBase, "lib")] : [] 16 | 17 | references: [ 18 | "app", 19 | "tests", 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(auto) 2 | add_subdirectory(manual) 3 | -------------------------------------------------------------------------------- /tests/auto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(addresscache) 2 | add_subdirectory(elfmap) 3 | add_subdirectory(kallsyms) 4 | add_subdirectory(perfdata) 5 | add_subdirectory(perfstdin) 6 | add_subdirectory(finddebugsym) 7 | -------------------------------------------------------------------------------- /tests/auto/addresscache/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_qtc_test(tst_addresscache 2 | DEPENDS Qt::Core Qt::Test perfparser_lib 3 | SOURCES tst_addresscache.cpp 4 | ) 5 | -------------------------------------------------------------------------------- /tests/auto/addresscache/addresscache.pro: -------------------------------------------------------------------------------- 1 | QT += testlib 2 | QT -= gui 3 | 4 | CONFIG += testcase strict_flags warn_on 5 | 6 | INCLUDEPATH += ../../../app 7 | 8 | TARGET = tst_addresscache 9 | 10 | include(../../../elfutils.pri) 11 | 12 | SOURCES += \ 13 | tst_addresscache.cpp \ 14 | ../../../app/perfelfmap.cpp \ 15 | ../../../app/perfaddresscache.cpp \ 16 | ../../../app/perfdwarfdiecache.cpp 17 | 18 | HEADERS += \ 19 | ../../../app/perfelfmap.h \ 20 | ../../../app/perfaddresscache.h \ 21 | ../../../app/perfdwarfdiecache.h 22 | 23 | OTHER_FILES += addresscache.qbs 24 | -------------------------------------------------------------------------------- /tests/auto/addresscache/addresscache.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | QtcAutotest { 4 | name: "AddressCache Autotest" 5 | files: [ 6 | "tst_addresscache.cpp", 7 | "../../../app/demangler.cpp", 8 | "../../../app/demangler.h", 9 | "../../../app/perfelfmap.cpp", 10 | "../../../app/perfelfmap.h", 11 | "../../../app/perfaddresscache.cpp", 12 | "../../../app/perfaddresscache.h", 13 | "../../../app/perfdwarfdiecache.cpp", 14 | "../../../app/perfdwarfdiecache.h", 15 | ] 16 | cpp.includePaths: base.concat(["../../../app"]).concat(project.includePaths) 17 | cpp.libraryPaths: project.libPaths 18 | cpp.dynamicLibraries: ["dw", "elf"] 19 | } 20 | -------------------------------------------------------------------------------- /tests/auto/addresscache/tst_addresscache.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "perfaddresscache.h" 26 | 27 | class TestAddressCache : public QObject 28 | { 29 | Q_OBJECT 30 | private slots: 31 | void testRelative() 32 | { 33 | PerfElfMap::ElfInfo info_a{{}, 0x100, 100, 0, 34 | QByteArrayLiteral("libfoo.so"), 35 | QByteArrayLiteral("/usr/lib/libfoo.so")}; 36 | PerfElfMap::ElfInfo info_b = info_a; 37 | info_b.addr = 0x200; 38 | 39 | PerfAddressCache cache; 40 | PerfAddressCache::OffsetAddressCache invalidAddressCache; 41 | PerfAddressCache::AddressCacheEntry entry{42, true}; 42 | cache.cache(info_a, 0x110, entry, &invalidAddressCache); 43 | QCOMPARE(cache.find(info_a, 0x110, &invalidAddressCache).locationId, entry.locationId); 44 | QCOMPARE(cache.find(info_b, 0x210, &invalidAddressCache).locationId, entry.locationId); 45 | } 46 | 47 | void testInvalid() 48 | { 49 | PerfAddressCache cache; 50 | PerfAddressCache::OffsetAddressCache invalidAddressCache_a; 51 | PerfAddressCache::OffsetAddressCache invalidAddressCache_b; 52 | PerfAddressCache::AddressCacheEntry entry{42, true}; 53 | cache.cache(PerfElfMap::ElfInfo{}, 0x110, entry, &invalidAddressCache_a); 54 | QCOMPARE(cache.find(PerfElfMap::ElfInfo{}, 0x110, &invalidAddressCache_a).locationId, entry.locationId); 55 | QCOMPARE(cache.find(PerfElfMap::ElfInfo{}, 0x110, &invalidAddressCache_b).locationId, -1); 56 | } 57 | 58 | void testEmpty() 59 | { 60 | PerfAddressCache cache; 61 | PerfAddressCache::OffsetAddressCache invalidAddressCache; 62 | QCOMPARE(cache.find(PerfElfMap::ElfInfo{}, 0x123, &invalidAddressCache).locationId, -1); 63 | } 64 | 65 | void testSymbolCache() 66 | { 67 | const auto libfoo_a = QByteArrayLiteral("/usr/lib/libfoo_a.so"); 68 | const auto libfoo_b = QByteArrayLiteral("/usr/lib/libfoo_b.so"); 69 | 70 | PerfAddressCache cache; 71 | 72 | QVERIFY(!cache.findSymbol(libfoo_a, 0).isValid()); 73 | QVERIFY(!cache.findSymbol(libfoo_b, 0).isValid()); 74 | 75 | cache.setSymbolCache(libfoo_a, {{0x100, 0x100, 10, "Foo"}, {0x11a, 0x11a, 0, "FooZ"}, {0x12a, 0x12a, 10, "FooN"}}); 76 | for (auto addr : {0x100, 0x100 + 9}) { 77 | const auto cached = cache.findSymbol(libfoo_a, addr); 78 | QVERIFY(cached.isValid()); 79 | QCOMPARE(cached.offset, quint64(0x100)); 80 | QCOMPARE(cached.size, quint64(10)); 81 | QCOMPARE(cached.symname, "Foo"); 82 | } 83 | QVERIFY(!cache.findSymbol(libfoo_a, 0x100 + 10).isValid()); 84 | QVERIFY(!cache.findSymbol(libfoo_b, 0x100).isValid()); 85 | QVERIFY(!cache.findSymbol(libfoo_b, 0x100 + 9).isValid()); 86 | QVERIFY(cache.findSymbol(libfoo_a, 0x11a + 1).isValid()); 87 | } 88 | }; 89 | 90 | QTEST_GUILESS_MAIN(TestAddressCache) 91 | 92 | #include "tst_addresscache.moc" 93 | -------------------------------------------------------------------------------- /tests/auto/auto.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = \ 3 | addresscache \ 4 | elfmap \ 5 | kallsyms \ 6 | perfdata \ 7 | perfstdin \ 8 | finddebugsym 9 | 10 | OTHER_FILES += auto.qbs 11 | -------------------------------------------------------------------------------- /tests/auto/auto.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | Project { 4 | name: "PerfParserAutotests" 5 | condition: project.withAutotests 6 | references: [ 7 | "addresscache", "elfmap", "kallsyms", "perfdata", "perfstdin", "finddebugsym" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tests/auto/elfmap/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_qtc_test(tst_elfmap 2 | DEPENDS Qt::Core Qt::Test perfparser_lib 3 | SOURCES tst_elfmap.cpp 4 | ) 5 | -------------------------------------------------------------------------------- /tests/auto/elfmap/elfmap.pro: -------------------------------------------------------------------------------- 1 | QT += testlib 2 | QT -= gui 3 | 4 | CONFIG += testcase strict_flags warn_on 5 | 6 | INCLUDEPATH += ../../../app 7 | 8 | TARGET = tst_elfmap 9 | 10 | SOURCES += \ 11 | tst_elfmap.cpp \ 12 | ../../../app/perfelfmap.cpp 13 | 14 | HEADERS += \ 15 | ../../../app/perfelfmap.h 16 | 17 | OTHER_FILES += elfmap.qbs 18 | -------------------------------------------------------------------------------- /tests/auto/elfmap/elfmap.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | QtcAutotest { 4 | name: "Elfmap Autotest" 5 | files: [ 6 | "tst_elfmap.cpp", 7 | "../../../app/perfelfmap.cpp", 8 | "../../../app/perfelfmap.h" 9 | ] 10 | cpp.includePaths: base.concat(["../../../app"]) 11 | } 12 | -------------------------------------------------------------------------------- /tests/auto/elfmap/tst_elfmap.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include "perfelfmap.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | QT_BEGIN_NAMESPACE 28 | namespace QTest { 29 | template<> 30 | char *toString(const PerfElfMap::ElfInfo &info) 31 | { 32 | QString string; 33 | QDebug stream(&string); 34 | stream << info; 35 | return qstrdup(qPrintable(string)); 36 | } 37 | } 38 | QT_END_NAMESPACE 39 | 40 | class TestElfMap : public QObject 41 | { 42 | Q_OBJECT 43 | private slots: 44 | void testNoOverlap() 45 | { 46 | const PerfElfMap::ElfInfo invalid; 47 | 48 | PerfElfMap map; 49 | QVERIFY(map.isEmpty()); 50 | 51 | const PerfElfMap::ElfInfo first({}, 100, 10, 0, "foo", "/foo"); 52 | 53 | QVERIFY(registerElf(&map, first).isEmpty()); 54 | QVERIFY(!map.isEmpty()); 55 | 56 | QCOMPARE(map.findElf(99), invalid); 57 | QCOMPARE(map.findElf(100), first); 58 | QCOMPARE(map.findElf(105), first); 59 | QCOMPARE(map.findElf(109), first); 60 | QCOMPARE(map.findElf(110), invalid); 61 | 62 | const PerfElfMap::ElfInfo second({}, 0, 10, 0, "bar", "/bar"); 63 | QVERIFY(registerElf(&map, second).isEmpty()); 64 | 65 | QCOMPARE(map.findElf(0), second); 66 | QCOMPARE(map.findElf(5), second); 67 | QCOMPARE(map.findElf(9), second); 68 | QCOMPARE(map.findElf(10), invalid); 69 | 70 | QCOMPARE(map.findElf(99), invalid); 71 | QCOMPARE(map.findElf(100), first); 72 | QCOMPARE(map.findElf(105), first); 73 | QCOMPARE(map.findElf(109), first); 74 | QCOMPARE(map.findElf(110), invalid); 75 | } 76 | 77 | void testOverwrite() 78 | { 79 | QFETCH(bool, firstIsFile); 80 | QFETCH(bool, secondIsFile); 81 | 82 | QTemporaryFile tmpFile1; 83 | if (firstIsFile) { 84 | QVERIFY(tmpFile1.open()); 85 | } 86 | QFileInfo file1(tmpFile1.fileName()); 87 | QCOMPARE(file1.isFile(), firstIsFile); 88 | 89 | QTemporaryFile tmpFile2; 90 | if (secondIsFile) { 91 | QVERIFY(tmpFile2.open()); 92 | } 93 | QFileInfo file2(tmpFile2.fileName()); 94 | QCOMPARE(file2.isFile(), secondIsFile); 95 | 96 | PerfElfMap map; 97 | 98 | const PerfElfMap::ElfInfo first(file1, 95, 20, 0); 99 | QVERIFY(registerElf(&map, first).isEmpty()); 100 | QCOMPARE(map.findElf(110), first); 101 | 102 | PerfElfMap::ElfInfo second(file1, 105, 20, 0); 103 | QCOMPARE(registerElf(&map, second), QList{first}); 104 | if (firstIsFile) 105 | second.baseAddr = first.addr; 106 | QCOMPARE(map.findElf(110), second); 107 | 108 | const PerfElfMap::ElfInfo fragment1(file1, 95, 10, 0); 109 | QCOMPARE(map.findElf(97), fragment1); 110 | 111 | const PerfElfMap::ElfInfo third(file2, 100, 20, 0); 112 | QList invalidatedByThird = {fragment1, second}; 113 | QCOMPARE(registerElf(&map, third), invalidatedByThird); 114 | QCOMPARE(map.findElf(110), third); 115 | QCOMPARE(map.findElf(110), third); 116 | 117 | const PerfElfMap::ElfInfo fragment2(file1, 120, 5, 15); 118 | const PerfElfMap::ElfInfo fragment3(file1, 95, 5, 0); 119 | QCOMPARE(map.findElf(122), fragment2); 120 | QCOMPARE(map.findElf(97), fragment3); 121 | } 122 | 123 | void testOverwrite_data() 124 | { 125 | QTest::addColumn("firstIsFile"); 126 | QTest::addColumn("secondIsFile"); 127 | 128 | QTest::newRow("both-files") << true << true; 129 | QTest::newRow("one-file-A") << false << true; 130 | QTest::newRow("one-file-B") << true << false; 131 | QTest::newRow("no-files") << false << false; 132 | } 133 | 134 | void testIsAddressInRange() 135 | { 136 | PerfElfMap map; 137 | QVERIFY(!map.isAddressInRange(10)); 138 | 139 | const PerfElfMap::ElfInfo first({}, 10, 10, 0); 140 | QVERIFY(registerElf(&map, first).isEmpty()); 141 | QVERIFY(!map.isAddressInRange(9)); 142 | QVERIFY(map.isAddressInRange(10)); 143 | QVERIFY(map.isAddressInRange(19)); 144 | QVERIFY(!map.isAddressInRange(20)); 145 | 146 | const PerfElfMap::ElfInfo second({}, 30, 10, 0); 147 | QVERIFY(registerElf(&map, second).isEmpty()); 148 | QVERIFY(!map.isAddressInRange(9)); 149 | QVERIFY(map.isAddressInRange(10)); 150 | QVERIFY(map.isAddressInRange(19)); 151 | QVERIFY(map.isAddressInRange(30)); 152 | QVERIFY(map.isAddressInRange(39)); 153 | QVERIFY(!map.isAddressInRange(40)); 154 | // gaps are also within range 155 | QVERIFY(map.isAddressInRange(20)); 156 | QVERIFY(map.isAddressInRange(29)); 157 | } 158 | 159 | void testExtendMapping() 160 | { 161 | QTemporaryFile file; 162 | QVERIFY(file.open()); 163 | const auto fileInfo = QFileInfo(file.fileName()); 164 | 165 | PerfElfMap map; 166 | const PerfElfMap::ElfInfo first(fileInfo, 0, 5000, 0); 167 | registerElf(&map, first); 168 | QCOMPARE(map.findElf(100), first); 169 | 170 | // fully contained in the first mapping 171 | const PerfElfMap::ElfInfo second(fileInfo, 20, 500, 20); 172 | registerElf(&map, second); 173 | QCOMPARE(map.findElf(100), first); 174 | 175 | // extend the first mapping 176 | const PerfElfMap::ElfInfo third(fileInfo, 2000, 8000, 2000); 177 | registerElf(&map, third); 178 | const PerfElfMap::ElfInfo extended(fileInfo, 0, 10000, 0); 179 | QCOMPARE(map.findElf(100), extended); 180 | QCOMPARE(map.findElf(2200), extended); 181 | 182 | // this has a gap, so don't extend directly 183 | PerfElfMap::ElfInfo fourth(fileInfo, 12000, 100, 100); 184 | registerElf(&map, fourth); 185 | QVERIFY(!fourth.hasBaseAddr()); 186 | fourth.baseAddr = 0; 187 | QVERIFY(fourth.hasBaseAddr()); 188 | QCOMPARE(map.findElf(12000), fourth); 189 | 190 | PerfElfMap::ElfInfo fifth(fileInfo, 2000, 500, 3000); 191 | QVERIFY(!fifth.hasBaseAddr()); // base addr will be set on registering based on first mmap. 192 | registerElf(&map, fifth); 193 | fifth.baseAddr = 0; 194 | QCOMPARE(map.findElf(2200), fifth); 195 | 196 | const PerfElfMap::ElfInfo remainder1(fileInfo, 0, 2000, 0); 197 | QCOMPARE(map.findElf(100), remainder1); 198 | 199 | const PerfElfMap::ElfInfo remainder2(fileInfo, 2500, 7500, 2500); 200 | QCOMPARE(map.findElf(3000), remainder2); 201 | } 202 | 203 | void benchRegisterElfDisjunct() 204 | { 205 | QFETCH(uint, numElfMaps); 206 | const quint64 ADDR_STEP = 1024; 207 | const quint64 MAX_ADDR = ADDR_STEP * numElfMaps; 208 | const quint64 LEN = 1024; 209 | QBENCHMARK { 210 | PerfElfMap map; 211 | for (quint64 addr = 0; addr < MAX_ADDR; addr += ADDR_STEP) { 212 | map.registerElf(addr, LEN, 0, {}); 213 | } 214 | } 215 | } 216 | 217 | void benchRegisterElfDisjunct_data() 218 | { 219 | QTest::addColumn("numElfMaps"); 220 | QTest::newRow("10") << 10u; 221 | QTest::newRow("100") << 100u; 222 | QTest::newRow("1000") << 1000u; 223 | QTest::newRow("2000") << 2000u; 224 | } 225 | 226 | void benchRegisterElfOverlapping() 227 | { 228 | QFETCH(uint, numElfMaps); 229 | const quint64 ADDR_STEP = 1024; 230 | const quint64 MAX_ADDR = ADDR_STEP * numElfMaps; 231 | quint64 len = MAX_ADDR; 232 | QBENCHMARK { 233 | PerfElfMap map; 234 | for (quint64 addr = 0; addr < MAX_ADDR; addr += ADDR_STEP, len -= ADDR_STEP) { 235 | map.registerElf(addr, len, 0, {}); 236 | } 237 | } 238 | } 239 | 240 | void benchRegisterElfOverlapping_data() 241 | { 242 | benchRegisterElfDisjunct_data(); 243 | } 244 | 245 | 246 | void benchRegisterElfExpanding() 247 | { 248 | QFETCH(uint, numElfMaps); 249 | const quint64 ADDR = 0; 250 | const quint64 LEN_STEP = 1024; 251 | const quint64 MAX_LEN = LEN_STEP * numElfMaps; 252 | QBENCHMARK { 253 | PerfElfMap map; 254 | for (quint64 len = LEN_STEP; len <= MAX_LEN; len += LEN_STEP) { 255 | map.registerElf(ADDR, len, 0, {}); 256 | } 257 | } 258 | } 259 | 260 | void benchRegisterElfExpanding_data() 261 | { 262 | benchRegisterElfDisjunct_data(); 263 | } 264 | 265 | void benchFindElfDisjunct() 266 | { 267 | QFETCH(uint, numElfMaps); 268 | 269 | PerfElfMap map; 270 | 271 | const quint64 ADDR_STEP = 1024; 272 | const quint64 MAX_ADDR = ADDR_STEP * numElfMaps; 273 | const quint64 LEN = 1024; 274 | for (quint64 addr = 0; addr < MAX_ADDR; addr += ADDR_STEP) { 275 | map.registerElf(addr, LEN, 0, {}); 276 | } 277 | 278 | const quint64 ADDR_STEP_FIND = 64; 279 | QBENCHMARK { 280 | for (quint64 addr = 0; addr < MAX_ADDR; addr += ADDR_STEP_FIND) { 281 | auto it = map.findElf(addr); 282 | Q_UNUSED(it); 283 | } 284 | } 285 | } 286 | 287 | void benchFindElfDisjunct_data() 288 | { 289 | benchRegisterElfDisjunct_data(); 290 | } 291 | 292 | void benchFindElfOverlapping() 293 | { 294 | QFETCH(uint, numElfMaps); 295 | 296 | PerfElfMap map; 297 | 298 | const quint64 ADDR_STEP = 1024; 299 | const quint64 MAX_ADDR = ADDR_STEP * numElfMaps; 300 | quint64 LEN = MAX_ADDR; 301 | for (quint64 addr = 0; addr < MAX_ADDR; addr += ADDR_STEP, LEN -= ADDR_STEP) { 302 | map.registerElf(addr, LEN, 0, {}); 303 | } 304 | 305 | const quint64 ADDR_STEP_FIND = 64; 306 | QBENCHMARK { 307 | for (quint64 addr = 0; addr < MAX_ADDR; addr += ADDR_STEP_FIND) { 308 | auto it = map.findElf(addr); 309 | Q_UNUSED(it); 310 | } 311 | } 312 | } 313 | 314 | void benchFindElfOverlapping_data() 315 | { 316 | benchRegisterElfDisjunct_data(); 317 | } 318 | 319 | void benchFindElfExpanding() 320 | { 321 | QFETCH(uint, numElfMaps); 322 | 323 | PerfElfMap map; 324 | 325 | const quint64 FIRST_ADDR = 0; 326 | const quint64 LEN_STEP = 1024; 327 | const quint64 MAX_LEN = LEN_STEP * numElfMaps; 328 | for (quint64 len = LEN_STEP; len <= MAX_LEN; len += LEN_STEP) { 329 | map.registerElf(FIRST_ADDR, len, 0, {}); 330 | } 331 | 332 | const quint64 MAX_ADDR = FIRST_ADDR + MAX_LEN; 333 | const quint64 ADDR_STEP_FIND = 64; 334 | QBENCHMARK { 335 | for (quint64 addr = FIRST_ADDR; addr < MAX_ADDR; addr += ADDR_STEP_FIND) { 336 | auto it = map.findElf(addr); 337 | Q_UNUSED(it); 338 | } 339 | } 340 | } 341 | 342 | void benchFindElfExpanding_data() 343 | { 344 | benchRegisterElfDisjunct_data(); 345 | } 346 | 347 | private: 348 | QList registerElf(PerfElfMap *map, const PerfElfMap::ElfInfo &info) 349 | { 350 | QList invalidated; 351 | auto connection = connect(map, &PerfElfMap::aboutToInvalidate, this, 352 | [&invalidated](const PerfElfMap::ElfInfo& other) { // clazy:exclude=lambda-in-connect 353 | invalidated.push_back(other); 354 | }); 355 | map->registerElf(info.addr, info.length, info.pgoff, info.localFile, 356 | info.originalFileName, info.originalPath); 357 | disconnect(connection); 358 | return invalidated; 359 | } 360 | }; 361 | 362 | QTEST_GUILESS_MAIN(TestElfMap) 363 | 364 | #include "tst_elfmap.moc" 365 | -------------------------------------------------------------------------------- /tests/auto/finddebugsym/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_qtc_test(tst_finddebugsym 2 | DEPENDS Qt::Core Qt::Test perfparser_lib 3 | SOURCES tst_finddebugsym.cpp 4 | ) 5 | -------------------------------------------------------------------------------- /tests/auto/finddebugsym/finddebugsym.pro: -------------------------------------------------------------------------------- 1 | QT += testlib 2 | QT -= gui 3 | 4 | CONFIG += testcase strict_flags warn_on 5 | 6 | INCLUDEPATH += ../../../app 7 | 8 | TARGET = tst_finddebugsym 9 | 10 | include(../../../elfutils.pri) 11 | 12 | SOURCES += \ 13 | tst_finddebugsym.cpp \ 14 | ../../../app/perfsymboltable.cpp 15 | 16 | HEADERS += \ 17 | ../../../app/perfsymboltable.h 18 | 19 | OTHER_FILES += finddebugsym.qbs 20 | -------------------------------------------------------------------------------- /tests/auto/finddebugsym/finddebugsym.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | QtcAutotest { 4 | name: "finddebugsym Autotest" 5 | files: [ 6 | "tst_finddebugsym.cpp", 7 | "../../../app/demangler.cpp", 8 | "../../../app/demangler.h", 9 | "../../../app/perfaddresscache.cpp", 10 | "../../../app/perfaddresscache.h", 11 | "../../../app/perfattributes.cpp", 12 | "../../../app/perfattributes.h", 13 | "../../../app/perfdata.cpp", 14 | "../../../app/perfdata.h", 15 | "../../../app/perfdwarfdiecache.cpp", 16 | "../../../app/perfdwarfdiecache.h", 17 | "../../../app/perfelfmap.cpp", 18 | "../../../app/perfelfmap.h", 19 | "../../../app/perffeatures.cpp", 20 | "../../../app/perffeatures.h", 21 | "../../../app/perffilesection.cpp", 22 | "../../../app/perffilesection.h", 23 | "../../../app/perfheader.cpp", 24 | "../../../app/perfheader.h", 25 | "../../../app/perfkallsyms.cpp", 26 | "../../../app/perfkallsyms.h", 27 | "../../../app/perfregisterinfo.cpp", 28 | "../../../app/perfregisterinfo.h", 29 | "../../../app/perfsymboltable.cpp", 30 | "../../../app/perfsymboltable.h", 31 | "../../../app/perftracingdata.cpp", 32 | "../../../app/perftracingdata.h", 33 | "../../../app/perfunwind.cpp", 34 | "../../../app/perfunwind.h", 35 | ] 36 | cpp.includePaths: base.concat(["../../../app"]).concat(project.includePaths) 37 | cpp.libraryPaths: project.libPaths 38 | cpp.dynamicLibraries: ["dw", "elf"] 39 | } 40 | -------------------------------------------------------------------------------- /tests/auto/finddebugsym/tst_finddebugsym.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Lieven Hey 4 | * 5 | ** Contact: http://www.qt.io/licensing/ 6 | ** 7 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 8 | ** 9 | ** GNU General Public License Usage 10 | ** This file may be used under the terms of the GNU General Public License 11 | ** version 3 as published by the Free Software Foundation and appearing in 12 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 13 | ** review the following information to ensure the GNU General Public License 14 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 15 | ** 16 | ** If you have questions regarding the use of this file, please use 17 | ** contact form at http://www.qt.io/contact-us 18 | ** 19 | ****************************************************************************/ 20 | 21 | #include "perfkallsyms.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include "perfsymboltable.h" 31 | 32 | class TestFindDebugSymbols : public QObject 33 | { 34 | Q_OBJECT 35 | private slots: 36 | void initTestCase() 37 | { 38 | const auto files = {QStringLiteral("/usr/bin/python3.8"), 39 | QStringLiteral("/usr/bin/.debug/096cdc8214a805dca8d174fe072684b0f21645.debug"), 40 | QStringLiteral("/usr/lib/libm.so"), 41 | QStringLiteral("/usr/lib/libqt.so"), 42 | QStringLiteral("/usr/lib/debug/libm.so"), 43 | QStringLiteral("/usr/lib/debug/lib/x64/libc.so"), 44 | QStringLiteral("/usr/lib/debug/lib/test.so"), 45 | QStringLiteral("/usr/lib/debug/usr/lib/test2.so")}; 46 | 47 | QDir dir(tempDir.path()); 48 | for (const auto& file : files) { 49 | auto path = QFileInfo(tempDir.path() + file).path(); 50 | dir.mkpath(path); 51 | QVERIFY(dir.exists(path)); 52 | 53 | QFile f(tempDir.path() + file); 54 | f.open(QIODevice::WriteOnly); 55 | f.write(file.toUtf8()); 56 | QVERIFY(f.exists()); 57 | } 58 | } 59 | 60 | void findDebugSymbols_data() 61 | { 62 | QTest::addColumn("root"); 63 | QTest::addColumn("file"); 64 | QTest::addColumn("debugLinkString"); 65 | 66 | QTest::addRow("/usr/bin") << QStringLiteral("/usr/bin/python3.8") 67 | << QStringLiteral("096cdc8214a805dca8d174fe072684b0f21645.debug") 68 | << QStringLiteral("/usr/bin/.debug/096cdc8214a805dca8d174fe072684b0f21645.debug"); 69 | QTest::addRow("/usr/lib/debug") << QStringLiteral("/usr/lib/libm.so") << QStringLiteral("libm.so") 70 | << QStringLiteral("/usr/lib/debug/libm.so"); 71 | QTest::addRow("/usr/lib/debug") << QStringLiteral("/usr/lib/x64/libc.so") << QStringLiteral("libc.so") 72 | << QStringLiteral("/usr/lib/debug/lib/x64/libc.so"); 73 | QTest::addRow("no debug file") << QStringLiteral("/usr/lib/libqt.so") << QStringLiteral("libqt.so") 74 | << QStringLiteral("/usr/lib/libqt.so"); 75 | QTest::addRow("/us/lib/") << QStringLiteral("/usr/lib/test.so") << QStringLiteral("test.so") 76 | << QStringLiteral("test.so"); 77 | QTest::addRow("/us/lib/") << QStringLiteral("/usr/lib/test2.so") << QStringLiteral("test2.so") 78 | << QStringLiteral("test2.so"); 79 | } 80 | 81 | void findDebugSymbols() 82 | { 83 | QFETCH(QString, root); 84 | QFETCH(QString, file); 85 | QFETCH(QString, debugLinkString); 86 | 87 | auto debugFile = PerfSymbolTable::findDebugInfoFile( 88 | tempDir.path() + QDir::separator(), root, file); 89 | 90 | QEXPECT_FAIL("/us/lib/", "Skipping broken test.", Continue); 91 | QCOMPARE(debugFile.absoluteFilePath(), QFileInfo(tempDir.path() + debugLinkString).absoluteFilePath()); 92 | } 93 | 94 | private: 95 | QTemporaryDir tempDir; 96 | }; 97 | 98 | QTEST_GUILESS_MAIN(TestFindDebugSymbols) 99 | 100 | #include "tst_finddebugsym.moc" 101 | -------------------------------------------------------------------------------- /tests/auto/kallsyms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_qtc_test(tst_kallsyms 2 | DEPENDS Qt::Core Qt::Test perfparser_lib 3 | SOURCES tst_kallsyms.cpp 4 | ) 5 | -------------------------------------------------------------------------------- /tests/auto/kallsyms/kallsyms.pro: -------------------------------------------------------------------------------- 1 | QT += testlib 2 | QT -= gui 3 | 4 | CONFIG += testcase strict_flags warn_on 5 | 6 | INCLUDEPATH += ../../../app 7 | 8 | TARGET = tst_kallsyms 9 | 10 | SOURCES += \ 11 | tst_kallsyms.cpp \ 12 | ../../../app/perfkallsyms.cpp 13 | 14 | HEADERS += \ 15 | ../../../app/perfkallsyms.h 16 | 17 | OTHER_FILES += kallsyms.qbs 18 | -------------------------------------------------------------------------------- /tests/auto/kallsyms/kallsyms.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | QtcAutotest { 4 | name: "Kallsyms Autotest" 5 | files: [ 6 | "tst_kallsyms.cpp", 7 | "../../../app/perfkallsyms.cpp", 8 | "../../../app/perfkallsyms.h" 9 | ] 10 | cpp.includePaths: base.concat(["../../../app"]) 11 | } 12 | -------------------------------------------------------------------------------- /tests/auto/kallsyms/tst_kallsyms.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include "perfkallsyms.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | class TestKallsyms : public QObject 28 | { 29 | Q_OBJECT 30 | private slots: 31 | void testResolve_data() 32 | { 33 | QTest::addColumn("kallsymsContents"); 34 | QTest::addColumn("address"); 35 | QTest::addColumn("expectedAddress"); 36 | QTest::addColumn("expectedSymbol"); 37 | QTest::addColumn("expectedModule"); 38 | QTest::addColumn("expectedFailsParse"); 39 | 40 | { 41 | const QByteArray kallsyms = 42 | "0000000000000000 A irq_stack_union\n" 43 | "0000000000000000 A __per_cpu_start\n" 44 | "ffffffff810002b8 T _stext\n" 45 | "ffffffff81001000 T hypercall_page\n" 46 | "ffffffff81001000 t xen_hypercall_set_trap_table\n" 47 | "ffffffff81001020 t xen_hypercall_mmu_update\n" 48 | "ffffffff81001040 t xen_hypercall_set_gdt\n" 49 | "ffffffffa0000e80 T serio_interrupt\t[serio]\n" 50 | "ffffffffa0000de0 T serio_unregister_driver\t[serio]\n"; 51 | 52 | QTest::newRow("__per_cpu_start:0") << kallsyms << 0x0ull 53 | << 0x0ull << QByteArray() << QByteArray() << false; 54 | QTest::newRow("_stext:0") << kallsyms << 0xffffffff810002b8ull 55 | << 0xffffffff810002b8ull << QByteArrayLiteral("_stext") << QByteArray() << false; 56 | QTest::newRow("_stext:2") << kallsyms << (0xffffffff810002b8ll + 0x2ull) 57 | << 0xffffffff810002b8ull << QByteArrayLiteral("_stext") << QByteArray() << false; 58 | QTest::newRow("xen_hypercall_set_gdt:0") << kallsyms << 0xffffffff81001040ull 59 | << 0xffffffff81001040ull << QByteArrayLiteral("xen_hypercall_set_gdt") << QByteArray() << false; 60 | QTest::newRow("xen_hypercall_set_gdt:256") << kallsyms << (0xffffffff81001040ull + 0x100ull) 61 | << 0xffffffff81001040ull << QByteArrayLiteral("xen_hypercall_set_gdt") << QByteArray() << false; 62 | QTest::newRow("xen_hypercall_set_gdt:256") << kallsyms << (0xffffffff81001040ull + 0x100ull) 63 | << 0xffffffff81001040ull << QByteArrayLiteral("xen_hypercall_set_gdt") << QByteArray() << false; 64 | QTest::newRow("serio_interrupt:0") << kallsyms << 0xffffffffa0000e80ull 65 | << 0xffffffffa0000e80ull << QByteArrayLiteral("serio_interrupt") << QByteArrayLiteral("[serio]") << false; 66 | { 67 | const auto kallsyms = QByteArrayLiteral("0000000000000000 A irq_stack_union"); 68 | QTest::newRow("zeros-only") << kallsyms << 0x0ull 69 | << 0x0ull << QByteArray() << QByteArray() << true; 70 | QTest::newRow("zeros-only2") << kallsyms << std::numeric_limits::max() 71 | << 0x0ull 72 | << QByteArray() << QByteArray() << true; 73 | } 74 | { 75 | const auto kallsyms = QByteArrayLiteral(" (null) A irq_stack_union"); 76 | QTest::newRow("null-only") << kallsyms << 0x0ull 77 | << 0x0ull << QByteArray() << QByteArray() << true; 78 | QTest::newRow("null-only2") << kallsyms << std::numeric_limits::max() 79 | << 0x0ull 80 | << QByteArrayLiteral("irq_stack_union") << QByteArray() << true; 81 | 82 | } 83 | } 84 | } 85 | 86 | void testResolve() 87 | { 88 | QFETCH(QByteArray, kallsymsContents); 89 | QFETCH(quint64, address); 90 | QFETCH(quint64, expectedAddress); 91 | QFETCH(QByteArray, expectedSymbol); 92 | QFETCH(QByteArray, expectedModule); 93 | QFETCH(bool, expectedFailsParse); 94 | 95 | QTemporaryFile file; 96 | QVERIFY(file.open()); 97 | file.write(kallsymsContents); 98 | file.flush(); 99 | 100 | PerfKallsyms kallsyms; 101 | QCOMPARE(kallsyms.parseMapping(file.fileName()), !expectedFailsParse); 102 | QVERIFY(kallsyms.errorString().isEmpty() == !expectedFailsParse); 103 | 104 | if (!expectedFailsParse) { 105 | const auto entry = kallsyms.findEntry(address); 106 | QCOMPARE(entry.address, expectedAddress); 107 | QCOMPARE(entry.symbol, expectedSymbol); 108 | QCOMPARE(entry.module, expectedModule); 109 | } 110 | } 111 | 112 | void testProc() 113 | { 114 | const auto path = QStringLiteral("/proc/kallsyms"); 115 | if (!QFileInfo::exists(path)) 116 | QSKIP("/proc/kallsysms not available"); 117 | 118 | auto checkSysCtl = [](const QString &knob, int maxValue) -> bool { 119 | QFile file(QLatin1String("/proc/sys/kernel/") + knob); 120 | if (!file.open(QIODevice::ReadOnly)) { 121 | qWarning() << "failed to open sysctl file for" << knob; 122 | return false; 123 | } 124 | const auto contents = file.readAll().trimmed(); 125 | bool ok = false; 126 | const auto value = contents.toInt(&ok); 127 | if (!ok) 128 | qWarning() << "Failed to parse sysctl file contents for" << knob << contents; 129 | return ok && value <= maxValue; 130 | }; 131 | if (!checkSysCtl(QStringLiteral("kptr_restrict"), 0)) { 132 | QEXPECT_FAIL("", "sysctl kernel.kptr_restrict > 0, cannot parse /proc/kallsyms", Abort); 133 | } else if (!checkSysCtl(QStringLiteral("perf_event_paranoid"), 1)) { 134 | QEXPECT_FAIL("", "sysctl kernel.perf_event_paranoid > 1, cannot parse /proc/kallsyms", Abort); 135 | } 136 | 137 | PerfKallsyms kallsyms; 138 | QVERIFY(kallsyms.parseMapping(path)); 139 | QVERIFY(kallsyms.errorString().isEmpty()); 140 | 141 | // just check that we find any entry 142 | const auto addr = std::numeric_limits::max(); 143 | const auto entry = kallsyms.findEntry(addr); 144 | QVERIFY(!entry.symbol.isEmpty()); 145 | } 146 | 147 | void testParseErrors() 148 | { 149 | QTemporaryFile file; 150 | QVERIFY(file.open()); 151 | const auto fileName = file.fileName(); 152 | 153 | { 154 | PerfKallsyms kallsyms; 155 | QVERIFY(!kallsyms.parseMapping(fileName)); 156 | qDebug() << kallsyms.errorString(); // file is empty 157 | QVERIFY(!kallsyms.errorString().isEmpty()); 158 | } 159 | 160 | file.write("this is garbage and not a valid mapping\n"); 161 | file.flush(); 162 | { 163 | PerfKallsyms kallsyms; 164 | QVERIFY(!kallsyms.parseMapping(fileName)); 165 | qDebug() << kallsyms.errorString(); // invalid address 166 | QVERIFY(!kallsyms.errorString().isEmpty()); 167 | } 168 | 169 | QVERIFY(file.remove()); 170 | { 171 | PerfKallsyms kallsyms; 172 | QVERIFY(!kallsyms.parseMapping(fileName)); 173 | qDebug() << kallsyms.errorString(); // file not found 174 | QVERIFY(!kallsyms.errorString().isEmpty()); 175 | } 176 | } 177 | 178 | void benchmarkProc() 179 | { 180 | const auto path = QStringLiteral("/proc/kallsyms"); 181 | if (!QFileInfo::exists(path)) 182 | QSKIP("/proc/kallsysms not available"); 183 | 184 | QBENCHMARK { 185 | PerfKallsyms kallsyms; 186 | bool parsed = kallsyms.parseMapping(path); 187 | Q_UNUSED(parsed); 188 | } 189 | } 190 | }; 191 | 192 | QTEST_GUILESS_MAIN(TestKallsyms) 193 | 194 | #include "tst_kallsyms.moc" 195 | -------------------------------------------------------------------------------- /tests/auto/perfdata/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_qtc_test(tst_perfdata 2 | DEPENDS Qt::Core Qt::Test perfparser_lib 3 | INCLUDES ../shared 4 | SOURCES 5 | ../shared/perfparsertestclient.cpp 6 | perfdata.qrc 7 | tst_perfdata.cpp 8 | ) 9 | -------------------------------------------------------------------------------- /tests/auto/perfdata/contentsize.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/contentsize.data -------------------------------------------------------------------------------- /tests/auto/perfdata/fork_static_gcc/fork.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/fork_static_gcc/fork.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/fork_static_gcc/perf.data.zstd.expected.txt.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/fork_static_gcc/perf.data.zstd.expected.txt.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/fork_static_gcc/perf.data.zstd.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/fork_static_gcc/perf.data.zstd.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/parallel_static_gcc/parallel_static_gcc.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/parallel_static_gcc/parallel_static_gcc.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/parallel_static_gcc/perf.data.zstd.expected.txt.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/parallel_static_gcc/perf.data.zstd.expected.txt.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/parallel_static_gcc/perf.data.zstd.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/parallel_static_gcc/perf.data.zstd.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/perfdata.pro: -------------------------------------------------------------------------------- 1 | include(../../../elfutils.pri) 2 | include(../shared/shared.pri) 3 | 4 | QT += testlib 5 | QT -= gui 6 | 7 | CONFIG += testcase strict_flags warn_on 8 | 9 | INCLUDEPATH += ../../../app 10 | 11 | TARGET = tst_perfdata 12 | 13 | SOURCES += \ 14 | tst_perfdata.cpp \ 15 | ../../../app/perfaddresscache.cpp \ 16 | ../../../app/perfattributes.cpp \ 17 | ../../../app/perfdata.cpp \ 18 | ../../../app/perfelfmap.cpp \ 19 | ../../../app/perffeatures.cpp \ 20 | ../../../app/perffilesection.cpp \ 21 | ../../../app/perfheader.cpp \ 22 | ../../../app/perfkallsyms.cpp \ 23 | ../../../app/perfregisterinfo.cpp \ 24 | ../../../app/perfsymboltable.cpp \ 25 | ../../../app/perftracingdata.cpp \ 26 | ../../../app/perfunwind.cpp \ 27 | ../../../app/perfdwarfdiecache.cpp \ 28 | ../../../app/demangle.cpp 29 | 30 | HEADERS += \ 31 | ../../../app/perfaddresscache.h \ 32 | ../../../app/perfattributes.h \ 33 | ../../../app/perfdata.h \ 34 | ../../../app/perfelfmap.h \ 35 | ../../../app/perffeatures.h \ 36 | ../../../app/perffilesection.h \ 37 | ../../../app/perfheader.h \ 38 | ../../../app/perfkallsyms.h \ 39 | ../../../app/perfregisterinfo.h \ 40 | ../../../app/perfsymboltable.h \ 41 | ../../../app/perftracingdata.h \ 42 | ../../../app/perfunwind.h \ 43 | ../../../app/perfdwarfdiecache.h \ 44 | ../../../app/demangle.h 45 | 46 | RESOURCES += \ 47 | perfdata.qrc 48 | 49 | OTHER_FILES += perfdata.qbs 50 | -------------------------------------------------------------------------------- /tests/auto/perfdata/perfdata.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | QtcAutotest { 4 | name: "PerfData Autotest" 5 | 6 | cpp.includePaths: ["/usr/include/elfutils", "../../../app", "../shared"] 7 | cpp.dynamicLibraries: ["dw", "elf"] 8 | 9 | files: [ 10 | "perfdata.qrc", 11 | "tst_perfdata.cpp", 12 | "../shared/perfparsertestclient.cpp", 13 | "../shared/perfparsertestclient.h", 14 | "../../../app/demangler.cpp", 15 | "../../../app/demangler.h", 16 | "../../../app/perfaddresscache.cpp", 17 | "../../../app/perfaddresscache.h", 18 | "../../../app/perfattributes.cpp", 19 | "../../../app/perfattributes.h", 20 | "../../../app/perfdata.cpp", 21 | "../../../app/perfdata.h", 22 | "../../../app/perfdwarfdiecache.cpp", 23 | "../../../app/perfdwarfdiecache.h", 24 | "../../../app/perfelfmap.cpp", 25 | "../../../app/perfelfmap.h", 26 | "../../../app/perffeatures.cpp", 27 | "../../../app/perffeatures.h", 28 | "../../../app/perffilesection.cpp", 29 | "../../../app/perffilesection.h", 30 | "../../../app/perfheader.cpp", 31 | "../../../app/perfheader.h", 32 | "../../../app/perfkallsyms.cpp", 33 | "../../../app/perfkallsyms.h", 34 | "../../../app/perfregisterinfo.cpp", 35 | "../../../app/perfregisterinfo.h", 36 | "../../../app/perfsymboltable.cpp", 37 | "../../../app/perfsymboltable.h", 38 | "../../../app/perftracingdata.cpp", 39 | "../../../app/perftracingdata.h", 40 | "../../../app/perfunwind.cpp", 41 | "../../../app/perfunwind.h", 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /tests/auto/perfdata/perfdata.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | probe.data.stream 4 | probe.data.file 5 | contentsize.data 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/auto/perfdata/probe.data.file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/probe.data.file -------------------------------------------------------------------------------- /tests/auto/perfdata/probe.data.stream: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/probe.data.stream -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_clang/perf.data.expected.txt.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_clang/perf.data.expected.txt.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_clang/perf.data.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_clang/perf.data.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_clang/vector_static_clang_v8.0.1.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_clang/vector_static_clang_v8.0.1.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_gcc/perf.data.expected.txt.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_gcc/perf.data.expected.txt.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_gcc/perf.data.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_gcc/perf.data.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_gcc/perf.data.zstd.expected.txt.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_gcc/perf.data.zstd.expected.txt.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_gcc/perf.data.zstd.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_gcc/perf.data.zstd.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_gcc/perf.lbr.data.expected.txt.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_gcc/perf.lbr.data.expected.txt.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_gcc/perf.lbr.data.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_gcc/perf.lbr.data.zlib -------------------------------------------------------------------------------- /tests/auto/perfdata/vector_static_gcc/vector_static_gcc_v9.1.0.zlib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-creator/perfparser/6af62f8a66ac3f9e09584d287a738cd1b3116395/tests/auto/perfdata/vector_static_gcc/vector_static_gcc_v9.1.0.zlib -------------------------------------------------------------------------------- /tests/auto/perfstdin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_qtc_test(tst_perfstdin 2 | DEPENDS Qt::Core Qt::Test perfparser_lib 3 | SOURCES tst_perfstdin.cpp 4 | ) 5 | -------------------------------------------------------------------------------- /tests/auto/perfstdin/perfstdin.pro: -------------------------------------------------------------------------------- 1 | QT += testlib 2 | QT -= gui 3 | 4 | CONFIG += testcase strict_flags warn_on 5 | 6 | INCLUDEPATH += ../../../app 7 | 8 | TARGET = tst_perfstdin 9 | 10 | SOURCES += \ 11 | tst_perfstdin.cpp \ 12 | ../../../app/perfstdin.cpp 13 | 14 | HEADERS += \ 15 | ../../../app/perfstdin.h 16 | 17 | OTHER_FILES += perfstdin.qbs 18 | -------------------------------------------------------------------------------- /tests/auto/perfstdin/perfstdin.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | QtcAutotest { 4 | name: "PerfStdin Autotest" 5 | files: [ 6 | "tst_perfstdin.cpp", 7 | "../../../app/perfstdin.cpp", 8 | "../../../app/perfstdin.h" 9 | ] 10 | cpp.includePaths: base.concat(["../../../app"]) 11 | } 12 | -------------------------------------------------------------------------------- /tests/auto/perfstdin/tst_perfstdin.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2018 The Qt Company Ltd 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include "perfstdin.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | class TestPerfstdin : public QObject 29 | { 30 | Q_OBJECT 31 | 32 | private slots: 33 | void testReadSelf(); 34 | }; 35 | 36 | void TestPerfstdin::testReadSelf() 37 | { 38 | std::freopen(QCoreApplication::applicationFilePath().toUtf8().constData(), "rb", stdin); 39 | QTemporaryFile tempfile; 40 | QVERIFY(tempfile.open()); 41 | PerfStdin device; 42 | int i = 0; 43 | 44 | auto doRead = [&](){ 45 | QVERIFY(device.bytesAvailable() > 0); 46 | qint64 r = device.bytesAvailable() + ((++i) % 32) - 16; 47 | const QByteArray data = device.read(r); 48 | qint64 pos = 0; 49 | while (pos < data.length()) { 50 | qint64 written = tempfile.write(data.data() + pos, data.length() - pos); 51 | QVERIFY(written >= 0); 52 | pos += written; 53 | } 54 | QCOMPARE(pos, data.length()); 55 | }; 56 | 57 | QObject::connect(&device, &QIODevice::readyRead, &tempfile, doRead); 58 | 59 | QObject::connect(&device, &QIODevice::aboutToClose, &tempfile, [&](){ 60 | while (device.bytesAvailable() > 0) 61 | doRead(); 62 | while (tempfile.bytesToWrite() > 0) 63 | QVERIFY(tempfile.flush()); 64 | 65 | QFile self(QCoreApplication::applicationFilePath()); 66 | QCOMPARE(tempfile.pos(), self.size()); 67 | 68 | QVERIFY(tempfile.reset()); 69 | QVERIFY(self.open(QIODevice::ReadOnly)); 70 | char c1, c2; 71 | while (!self.atEnd()) { 72 | QVERIFY(self.getChar(&c1)); 73 | QVERIFY(tempfile.getChar(&c2)); 74 | QCOMPARE(c1, c2); 75 | } 76 | QVERIFY(self.atEnd()); 77 | QVERIFY(tempfile.atEnd()); 78 | tempfile.close(); 79 | }); 80 | 81 | QVERIFY(device.open(QIODevice::ReadOnly)); 82 | QTRY_VERIFY(!tempfile.isOpen()); 83 | } 84 | 85 | QTEST_MAIN(TestPerfstdin) 86 | 87 | #include "tst_perfstdin.moc" 88 | -------------------------------------------------------------------------------- /tests/auto/shared/perfparsertestclient.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include "perffeatures.h" 21 | #include "perfparsertestclient.h" 22 | 23 | #include 24 | #include 25 | 26 | #ifdef MANUAL_TEST 27 | #define QVERIFY Q_ASSERT 28 | #define QCOMPARE(x, y) Q_ASSERT(x == y) 29 | #else 30 | #include 31 | #endif 32 | 33 | PerfParserTestClient::PerfParserTestClient(QObject *parent) : QObject(parent) 34 | { 35 | } 36 | 37 | void PerfParserTestClient::extractTrace(QIODevice *device) 38 | { 39 | QVERIFY(device->bytesAvailable() > 0); 40 | const char streamMagic[] = "QPERFSTREAM"; 41 | const int magicSize = sizeof(streamMagic); 42 | 43 | QVarLengthArray magic(magicSize); 44 | device->read(magic.data(), magicSize); 45 | QCOMPARE(QByteArray(magic.data(), magic.size()), QByteArray(streamMagic, magicSize)); 46 | 47 | qint32 version; 48 | device->read(reinterpret_cast(&version), sizeof(qint32)); 49 | version = qFromLittleEndian(version); 50 | 51 | QVERIFY(version == QDataStream::Qt_DefaultCompiledVersion); 52 | 53 | float progress = -1; 54 | 55 | auto checkString = [this](qint32 id) { 56 | QVERIFY(id < m_strings.length()); 57 | QVERIFY(!m_strings[id].isEmpty()); 58 | }; 59 | 60 | auto checkLocation = [this](qint32 id) { 61 | QVERIFY(id < m_locations.length()); 62 | QVERIFY(m_locations[id].pid != 0); 63 | }; 64 | 65 | auto checkAttribute = [this, &checkString](qint32 id) { 66 | QVERIFY(id < m_attributes.length()); 67 | checkString(m_attributes[id].name); 68 | }; 69 | 70 | while (device->bytesAvailable() >= static_cast(sizeof(quint32))) { 71 | qint32 size; 72 | device->read(reinterpret_cast(&size), sizeof(quint32)); 73 | size = qFromLittleEndian(size); 74 | 75 | QVERIFY(device->bytesAvailable() >= size); 76 | QDataStream stream(device->read(size)); 77 | 78 | quint8 eventType; 79 | stream >> eventType; 80 | 81 | switch (eventType) { 82 | case ThreadStart: { 83 | ThreadStartEvent threadStart; 84 | stream >> threadStart.pid >> threadStart.tid >> threadStart.time >> threadStart.cpu >> threadStart.ppid; 85 | m_threadStarts.append(threadStart); 86 | m_commands.insert(threadStart.pid, m_commands.value(threadStart.ppid)); 87 | break; 88 | } 89 | case ThreadEnd: { 90 | ThreadEndEvent threadEnd; 91 | stream >> threadEnd.pid >> threadEnd.tid >> threadEnd.time >> threadEnd.cpu; 92 | m_threadEnds.append(threadEnd); 93 | break; 94 | } 95 | case Command: { 96 | CommandEvent command; 97 | stream >> command.pid >> command.tid >> command.time >> command.cpu >> command.name; 98 | checkString(command.name); 99 | m_commands.insert(command.tid, command); 100 | if (command.pid != command.tid && !m_commands.contains(command.pid)) 101 | m_commands.insert(command.pid, command); 102 | break; 103 | } 104 | case LocationDefinition: { 105 | qint32 id; 106 | LocationEvent location; 107 | stream >> id >> location.address >> location.file >> location.pid >> location.line 108 | >> location.column >> location.parentLocationId >> location.relAddr; 109 | if (location.file != -1) 110 | checkString(location.file); 111 | if (location.parentLocationId != -1) 112 | checkLocation(location.parentLocationId); 113 | QCOMPARE(id, m_locations.length()); 114 | m_locations.append(location); 115 | break; 116 | } 117 | case SymbolDefinition: { 118 | qint32 id; 119 | SymbolEvent symbol; 120 | stream >> id >> symbol.name >> symbol.binary >> symbol.path >> symbol.isKernel >> symbol.relAddr >> symbol.size >> symbol.actualPath; 121 | if (symbol.name != -1) 122 | checkString(symbol.name); 123 | if (symbol.binary != -1) 124 | checkString(symbol.binary); 125 | QVERIFY(id < m_locations.size()); 126 | m_symbols.insert(id, symbol); 127 | break; 128 | } 129 | case AttributesDefinition: { 130 | qint32 id; 131 | AttributeEvent attribute; 132 | stream >> id >> attribute.type >> attribute.config >> attribute.name 133 | >> attribute.usesFrequency >> attribute.frequencyOrPeriod; 134 | checkString(attribute.name); 135 | QCOMPARE(id, m_attributes.length()); 136 | m_attributes.append(attribute); 137 | break; 138 | } 139 | case StringDefinition: { 140 | qint32 id; 141 | QByteArray string; 142 | stream >> id >> string; 143 | QCOMPARE(id, m_strings.length()); 144 | m_strings.append(string); 145 | break; 146 | } 147 | case Error: { 148 | qint32 errorCode; 149 | QString message; 150 | stream >> errorCode >> message; 151 | // Ignore this: We cannot find the elfs of course. 152 | break; 153 | } 154 | case Sample: 155 | case TracePointSample: { 156 | SampleEvent sample; 157 | stream >> sample.pid >> sample.tid >> sample.time >> sample.cpu >> sample.frames 158 | >> sample.numGuessedFrames >> sample.values; 159 | for (qint32 locationId : std::as_const(sample.frames)) 160 | checkLocation(locationId); 161 | for (const auto &value : std::as_const(sample.values)) 162 | checkAttribute(value.first); 163 | 164 | if (eventType == TracePointSample) { 165 | stream >> sample.tracePointData; 166 | for (auto it = sample.tracePointData.constBegin(), 167 | end = sample.tracePointData.constEnd(); 168 | it != end; ++it) { 169 | checkString(it.key()); 170 | } 171 | } 172 | 173 | m_samples.append(sample); 174 | break; 175 | } 176 | case Progress: { 177 | const float oldProgress = progress; 178 | stream >> progress; 179 | QVERIFY(progress > oldProgress); 180 | break; 181 | } 182 | case TracePointFormat: { 183 | qint32 id; 184 | TracePointFormatEvent tracePointFormat; 185 | stream >> id >> tracePointFormat.system >> tracePointFormat.name 186 | >> tracePointFormat.flags; 187 | checkString(tracePointFormat.system); 188 | checkString(tracePointFormat.name); 189 | QVERIFY(!m_tracePointFormats.contains(id)); 190 | m_tracePointFormats.insert(id, tracePointFormat); 191 | break; 192 | } 193 | default: 194 | stream.skipRawData(size); 195 | break; 196 | } 197 | QVERIFY(stream.atEnd()); 198 | } 199 | } 200 | 201 | void PerfParserTestClient::convertToText(QTextStream &out) const 202 | { 203 | using Qt::dec; 204 | using Qt::hex; 205 | const auto allSamples = samples(); 206 | for (const auto &sample : allSamples) { 207 | out << string(command(sample.pid).name) << '\t' 208 | << sample.pid << '\t' << sample.tid << '\t' 209 | << sample.time / 1000000000 << '.' << qSetFieldWidth(9) << qSetPadChar(QLatin1Char('0')) 210 | << sample.time % 1000000000 << qSetFieldWidth(0) << qSetPadChar(QLatin1Char(' ')) << '\n'; 211 | for (const auto &value : sample.values) { 212 | const auto attribute = this->attribute(value.first); 213 | const auto cost = attribute.usesFrequency ? value.second : attribute.frequencyOrPeriod; 214 | out << '\t' << string(attribute.name) << ": "; 215 | if (attribute.type == 2) { 216 | const auto format = tracePointFormat(static_cast(attribute.config)); 217 | out << string(format.system) << ' ' << string(format.name) << ' ' << Qt::hex << format.flags << Qt::dec << '\n'; 218 | // we need stable output to allow comparisons, so convert to map 219 | QMap sortedTracePointData; 220 | for (auto it = sample.tracePointData.begin(); it != sample.tracePointData.end(); ++it) { 221 | sortedTracePointData.insert(it.key(), it.value()); 222 | } 223 | for (auto it = sortedTracePointData.begin(); it != sortedTracePointData.end(); ++it) { 224 | out << "\t\t" << string(it.key()) << '=' << it.value().toString() << '\n'; 225 | } 226 | } else { 227 | out << cost << '\n'; 228 | } 229 | } 230 | out << '\n'; 231 | auto printFrame = [&out, this](qint32 locationId) -> qint32 { 232 | const auto location = this->location(locationId); 233 | out << '\t' << Qt::hex << location.address << ' ' << location.relAddr << Qt::dec; 234 | const auto symbol = this->symbol(locationId); 235 | if (location.file != -1) 236 | out << '\t' << string(location.file) << ':' << location.line << ':' << location.column; 237 | 238 | out << '\t' << Qt::hex << symbol.relAddr << ' ' << symbol.size << Qt::dec; 239 | if (symbol.path != -1) 240 | out << '\t' << string(symbol.name) << ' ' << string(symbol.binary) << ' ' << string(symbol.path) << ' ' << (symbol.isKernel ? "[kernel]" : ""); 241 | out << '\n'; 242 | return location.parentLocationId; 243 | }; 244 | for (const auto &frame : sample.frames) { 245 | auto locationId = printFrame(frame); 246 | while (locationId != -1) 247 | locationId = printFrame(locationId); 248 | } 249 | out << '\n'; 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /tests/auto/shared/perfparsertestclient.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | QT_BEGIN_NAMESPACE 29 | class QTextStream; 30 | QT_END_NAMESPACE 31 | 32 | class PerfParserTestClient : public QObject 33 | { 34 | Q_OBJECT 35 | public: 36 | struct AttributeEvent { 37 | quint32 type = 0; 38 | qint32 name = -1; 39 | quint64 config = 0; 40 | bool usesFrequency = false; 41 | quint64 frequencyOrPeriod = 0; 42 | }; 43 | 44 | struct ThreadEvent { 45 | qint32 pid = -1; 46 | qint32 tid = -1; 47 | quint64 time = 0; 48 | quint32 cpu = 0; 49 | }; 50 | 51 | struct ThreadEndEvent : public ThreadEvent 52 | { 53 | }; 54 | 55 | struct ThreadStartEvent : public ThreadEvent 56 | { 57 | qint32 ppid = -1; 58 | }; 59 | 60 | struct CommandEvent : public ThreadEvent { 61 | qint32 name = -1; 62 | }; 63 | 64 | struct LocationEvent { 65 | quint64 address = 0; 66 | quint64 relAddr = 0; 67 | qint32 file = -1; 68 | quint32 pid = 0; 69 | qint32 line = -1; 70 | qint32 column = -1; 71 | qint32 parentLocationId = -1; 72 | }; 73 | 74 | struct SymbolEvent { 75 | qint32 name = -1; 76 | quint64 relAddr = 0; 77 | quint64 size = 0; 78 | qint32 binary = -1; 79 | qint32 path = -1; 80 | qint32 actualPath = -1; 81 | bool isKernel = false; 82 | }; 83 | 84 | struct SampleEvent : public ThreadEvent { 85 | QVector frames; 86 | QVector> values; 87 | QHash tracePointData; 88 | quint8 numGuessedFrames = 0; 89 | }; 90 | 91 | struct TracePointFormatEvent { 92 | qint32 system = -1; 93 | qint32 name = -1; 94 | quint32 flags = 0; 95 | }; 96 | 97 | // Repeated here, as we want to check against accidental changes in enum values. 98 | enum EventType { 99 | ThreadStart, 100 | ThreadEnd, 101 | Command, 102 | LocationDefinition, 103 | SymbolDefinition, 104 | StringDefinition, 105 | LostDefinition, 106 | FeaturesDefinition, 107 | Error, 108 | Progress, 109 | TracePointFormat, 110 | AttributesDefinition, 111 | ContextSwitchDefinition, 112 | Sample, 113 | TracePointSample, 114 | InvalidType 115 | }; 116 | Q_ENUM(EventType) 117 | 118 | explicit PerfParserTestClient(QObject *parent = nullptr); 119 | 120 | void extractTrace(QIODevice *output); 121 | 122 | QByteArray string(qint32 id) const { return m_strings.value(id); } 123 | CommandEvent command(qint32 tid) const { return m_commands[tid]; } 124 | AttributeEvent attribute(qint32 id) const { return m_attributes.value(id); } 125 | QVector samples() const { return m_samples; } 126 | LocationEvent location(qint32 id) const { return m_locations.value(id); } 127 | SymbolEvent symbol(qint32 id) const { return m_symbols.value(id); } 128 | 129 | TracePointFormatEvent tracePointFormat(qint32 id) const { return m_tracePointFormats.value(id); } 130 | 131 | void convertToText(QTextStream &output) const; 132 | 133 | private: 134 | QVector m_strings; 135 | QVector m_attributes; 136 | QHash m_commands; 137 | QVector m_threadStarts; 138 | QVector m_threadEnds; 139 | QVector m_locations; 140 | QHash m_symbols; 141 | QVector m_samples; 142 | QHash m_tracePointFormats; 143 | }; 144 | -------------------------------------------------------------------------------- /tests/auto/shared/shared.pri: -------------------------------------------------------------------------------- 1 | INCLUDEPATH += $$PWD 2 | 3 | HEADERS += \ 4 | $$PWD/perfparsertestclient.h 5 | 6 | SOURCES += \ 7 | $$PWD/perfparsertestclient.cpp 8 | -------------------------------------------------------------------------------- /tests/manual/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(perf2text) 2 | -------------------------------------------------------------------------------- /tests/manual/clients/fork.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | pid_t child = fork(); 12 | if (child == 0) { 13 | double sum = 0; 14 | for (int i = 0; i < 1000000; ++i) { 15 | sum += cos(cos(i) * cos(i) + cos(i * i) + cos(cos(i))); 16 | } 17 | printf("sum is: %g\n", sum); 18 | } else { 19 | printf("waiting for child\n"); 20 | waitpid(child, NULL, 0); 21 | printf("done waiting\n"); 22 | } 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/manual/clients/parallel.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | double worker() 15 | { 16 | uniform_real_distribution uniform(-1E5, 1E5); 17 | default_random_engine engine; 18 | double s = 0; 19 | for (int i = 0; i < 10000; ++i) { 20 | s += norm(complex(uniform(engine), uniform(engine))); 21 | } 22 | cout << s << endl; 23 | return s; 24 | } 25 | 26 | int main(int argc, char** argv) 27 | { 28 | const int numTasks = argc > 1 ? stoi(argv[1]) : std::thread::hardware_concurrency(); 29 | vector> results; 30 | for (int i = 0; i < numTasks; ++i) { 31 | results.push_back(async(launch::async, [i]() { 32 | return worker() * i; 33 | })); 34 | } 35 | return 0; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /tests/manual/clients/vector.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | std::vector v; 12 | std::generate_n(std::back_inserter(v), 100000, [i = 0] () mutable { 13 | auto x = std::sin(i++); 14 | auto y = std::cos(i++); 15 | return std::abs(std::complex(x, y)); 16 | }); 17 | auto sum = std::accumulate(v.begin(), v.end(), 0.0); 18 | return sum > 0; 19 | } 20 | -------------------------------------------------------------------------------- /tests/manual/manual.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = perf2text 3 | 4 | OTHER_FILES += manual.qbs 5 | 6 | -------------------------------------------------------------------------------- /tests/manual/manual.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | Project { 4 | name: "Manual Tests" 5 | references: ["perf2text"] 6 | } 7 | 8 | -------------------------------------------------------------------------------- /tests/manual/perf2text/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_qtc_executable(perf2text 2 | DEFINES MANUAL_TEST 3 | DEPENDS perfparser_lib 4 | INCLUDES ../../auto/shared/ 5 | SOURCES 6 | ../../auto/shared/perfparsertestclient.cpp 7 | perf2text.cpp 8 | ) 9 | -------------------------------------------------------------------------------- /tests/manual/perf2text/perf2text.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the Qt Enterprise Perf Profiler Add-on. 7 | ** 8 | ** GNU General Public License Usage 9 | ** This file may be used under the terms of the GNU General Public License 10 | ** version 3 as published by the Free Software Foundation and appearing in 11 | ** the file LICENSE.GPLv3 included in the packaging of this file. Please 12 | ** review the following information to ensure the GNU General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/gpl.html. 14 | ** 15 | ** If you have questions regarding the use of this file, please use 16 | ** contact form at http://www.qt.io/contact-us 17 | ** 18 | ****************************************************************************/ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "perfparsertestclient.h" 27 | 28 | int main(int argc, char **argv) 29 | { 30 | QCoreApplication app(argc, argv); 31 | 32 | const auto perfparser = QStandardPaths::findExecutable(QStringLiteral("perfparser"), {app.applicationDirPath()}); 33 | if (perfparser.isEmpty()) { 34 | qWarning() << "failed to find perfparser executable"; 35 | return 1; 36 | } 37 | 38 | auto args = app.arguments(); 39 | args.removeFirst(); 40 | 41 | QProcess process; 42 | process.setProcessChannelMode(QProcess::ForwardedErrorChannel); 43 | QObject::connect(&process, &QProcess::errorOccurred, &app, [&process](QProcess::ProcessError error) { 44 | qWarning() << "perfparser process error:" << error << process.errorString(); 45 | }); 46 | QObject::connect(&process, QOverload::of(&QProcess::finished), 47 | &app, [&process](int exitCode, QProcess::ExitStatus status) { 48 | if (exitCode != 0 || status != QProcess::NormalExit) 49 | qWarning() << "perfparser process finished abnormally:" << exitCode << status << process.errorString(); 50 | }); 51 | process.start(perfparser, args); 52 | if (!process.waitForStarted() || !process.waitForFinished()) 53 | return 1; 54 | 55 | QTextStream out(stdout); 56 | 57 | PerfParserTestClient client; 58 | client.extractTrace(&process); 59 | client.convertToText(out); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /tests/manual/perf2text/perf2text.pro: -------------------------------------------------------------------------------- 1 | QT = core 2 | CONFIG += c++11 console 3 | CONFIG -= app_bundle 4 | 5 | CONFIG += strict_flags warn_on 6 | 7 | INCLUDEPATH += ../../../app 8 | 9 | include(../../../paths.pri) 10 | include(../../auto/shared/shared.pri) 11 | 12 | DEFINES += MANUAL_TEST 13 | DESTDIR = $$PERFPARSER_APP_DESTDIR 14 | target.path = $$PERFPARSER_APP_INSTALLDIR 15 | INSTALLS += target 16 | 17 | TARGET = perf2text 18 | 19 | SOURCES += perf2text.cpp 20 | 21 | OTHER_FILES += perf2text.qbs 22 | -------------------------------------------------------------------------------- /tests/manual/perf2text/perf2text.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | QtcTool { 4 | name: "perf2text" 5 | 6 | Depends { name: "perfparser" } 7 | 8 | cpp.defines: ["MANUAL_TEST"] 9 | cpp.includePaths: ["../../../app", "../../auto/shared"] 10 | 11 | files: [ 12 | "perf2text.cpp", 13 | "../../auto/shared/perfparsertestclient.cpp", 14 | "../../auto/shared/perfparsertestclient.h", 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tests/tests.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = auto manual 3 | 4 | OTHER_FILES += tests.qbs 5 | -------------------------------------------------------------------------------- /tests/tests.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | Project { 4 | name: "Tests" 5 | references: ["auto", "manual"] 6 | } 7 | --------------------------------------------------------------------------------