fix: breakpad use miniz
Some checks failed
sm-rpc / build (Debug, arm-linux-gnueabihf) (push) Successful in 1m34s
sm-rpc / build (Debug, aarch64-linux-gnu) (push) Successful in 2m46s
sm-rpc / build (Debug, host.gcc) (push) Failing after 1m28s
sm-rpc / build (Release, aarch64-linux-gnu) (push) Successful in 2m14s
sm-rpc / build (Release, arm-linux-gnueabihf) (push) Successful in 2m8s
sm-rpc / build (Debug, mipsel-linux-gnu) (push) Successful in 5m35s
sm-rpc / build (Release, host.gcc) (push) Failing after 1m55s
sm-rpc / build (Release, mipsel-linux-gnu) (push) Successful in 7m21s
Some checks failed
sm-rpc / build (Debug, arm-linux-gnueabihf) (push) Successful in 1m34s
sm-rpc / build (Debug, aarch64-linux-gnu) (push) Successful in 2m46s
sm-rpc / build (Debug, host.gcc) (push) Failing after 1m28s
sm-rpc / build (Release, aarch64-linux-gnu) (push) Successful in 2m14s
sm-rpc / build (Release, arm-linux-gnueabihf) (push) Successful in 2m8s
sm-rpc / build (Debug, mipsel-linux-gnu) (push) Successful in 5m35s
sm-rpc / build (Release, host.gcc) (push) Failing after 1m55s
sm-rpc / build (Release, mipsel-linux-gnu) (push) Successful in 7m21s
This commit is contained in:
30
third_party/tracy/update/CMakeLists.txt
vendored
Normal file
30
third_party/tracy/update/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
option(NO_ISA_EXTENSIONS "Disable ISA extensions (don't pass -march=native or -mcpu=native to the compiler)" OFF)
|
||||
|
||||
set(NO_STATISTICS ON)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/version.cmake)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
project(
|
||||
tracy-update
|
||||
LANGUAGES C CXX
|
||||
VERSION ${TRACY_VERSION_STRING}
|
||||
)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/config.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/vendor.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/server.cmake)
|
||||
|
||||
set(PROGRAM_FILES
|
||||
src/OfflineSymbolResolver.cpp
|
||||
src/OfflineSymbolResolverAddr2Line.cpp
|
||||
src/OfflineSymbolResolverDbgHelper.cpp
|
||||
src/update.cpp
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${PROGRAM_FILES} ${COMMON_FILES} ${SERVER_FILES})
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE TracyServer TracyGetOpt)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_LIST_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
|
171
third_party/tracy/update/src/OfflineSymbolResolver.cpp
vendored
Normal file
171
third_party/tracy/update/src/OfflineSymbolResolver.cpp
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unordered_map>
|
||||
#include <zstd.h>
|
||||
|
||||
#include "../../server/TracyWorker.hpp"
|
||||
|
||||
#include "OfflineSymbolResolver.h"
|
||||
|
||||
bool ApplyPathSubstitutions( std::string& path, const PathSubstitutionList& pathSubstitutionlist )
|
||||
{
|
||||
for( const auto& substitution : pathSubstitutionlist )
|
||||
{
|
||||
if( std::regex_search(path, substitution.first) )
|
||||
{
|
||||
path = std::regex_replace( path, substitution.first, substitution.second );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
tracy::StringIdx AddSymbolString( tracy::Worker& worker, const std::string& str )
|
||||
{
|
||||
// TODO: use string hash map to reduce potential string duplication?
|
||||
tracy::StringLocation location = worker.StoreString( str.c_str(), str.length() );
|
||||
return tracy::StringIdx( location.idx );
|
||||
}
|
||||
|
||||
bool PatchSymbolsWithRegex( tracy::Worker& worker, const PathSubstitutionList& pathSubstitutionlist, bool verbose )
|
||||
{
|
||||
uint64_t callstackFrameCount = worker.GetCallstackFrameCount();
|
||||
std::string relativeSoNameMatch = "[unresolved]";
|
||||
|
||||
std::cout << "Found " << callstackFrameCount << " callstack frames. Batching into image groups..." << std::endl;
|
||||
|
||||
// batch the symbol queries by .so so we issue the least amount of requests
|
||||
using FrameEntriesPerImageIdx = std::unordered_map<uint32_t, FrameEntryList>;
|
||||
FrameEntriesPerImageIdx entriesPerImageIdx;
|
||||
|
||||
auto& callstackFrameMap = worker.GetCallstackFrameMap();
|
||||
for( auto it = callstackFrameMap.begin(); it != callstackFrameMap.end(); ++it )
|
||||
{
|
||||
tracy::CallstackFrameData* frameDataPtr = it->second;
|
||||
if( !frameDataPtr )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tracy::CallstackFrameData& frameData = *frameDataPtr;
|
||||
const char* imageName = worker.GetString( frameData.imageName );
|
||||
|
||||
const uint32_t imageNameIdx = frameData.imageName.Idx();
|
||||
FrameEntryList& entries = entriesPerImageIdx[imageNameIdx];
|
||||
|
||||
for( uint8_t f = 0; f < frameData.size; f++ )
|
||||
{
|
||||
tracy::CallstackFrame& frame = frameData.data[f];
|
||||
|
||||
// TODO: use a better way to identify symbols that are unresolved
|
||||
const char* nameStr = worker.GetString(frame.name);
|
||||
if( strncmp( nameStr, relativeSoNameMatch.c_str(), relativeSoNameMatch.length() ) == 0 )
|
||||
{
|
||||
// when doing offline resolving we pass the offset from the start of the shared library in the "symAddr"
|
||||
const uint64_t decodedOffset = frame.symAddr;
|
||||
entries.push_back( {&frame, decodedOffset} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Batched into " << entriesPerImageIdx.size() << " unique image groups" << std::endl;
|
||||
|
||||
// FIXME: the resolving of symbols here can be slow and could be done in parallel per "image"
|
||||
// - be careful with string allocation though as that would be not safe to do in parallel
|
||||
for( FrameEntriesPerImageIdx::iterator imageIt = entriesPerImageIdx.begin(),
|
||||
imageItEnd = entriesPerImageIdx.end(); imageIt != imageItEnd; ++imageIt )
|
||||
{
|
||||
tracy::StringIdx imageIdx( imageIt->first );
|
||||
std::string imagePath = worker.GetString( imageIdx );
|
||||
|
||||
FrameEntryList& entries = imageIt->second;
|
||||
|
||||
if( !entries.size() ) continue;
|
||||
|
||||
std::cout << "Resolving " << entries.size() << " symbols for image: '"
|
||||
<< imagePath << "'" << std::endl;
|
||||
const bool substituted = ApplyPathSubstitutions( imagePath, pathSubstitutionlist );
|
||||
if( substituted )
|
||||
{
|
||||
std::cout << "\tPath substituted to: '" << imagePath << "'" << std::endl;
|
||||
}
|
||||
|
||||
SymbolEntryList resolvedEntries;
|
||||
ResolveSymbols( imagePath, entries, resolvedEntries );
|
||||
|
||||
if( resolvedEntries.size() != entries.size() )
|
||||
{
|
||||
std::cerr << " failed to resolve all entries! (got: "
|
||||
<< resolvedEntries.size() << ")" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// finally patch the string with the resolved symbol data
|
||||
for ( size_t i = 0; i < resolvedEntries.size(); ++i )
|
||||
{
|
||||
FrameEntry& frameEntry = entries[i];
|
||||
const SymbolEntry& symbolEntry = resolvedEntries[i];
|
||||
|
||||
tracy::CallstackFrame& frame = *frameEntry.frame;
|
||||
|
||||
if( !symbolEntry.name.length() ) continue;
|
||||
|
||||
if( verbose )
|
||||
{
|
||||
const char* nameStr = worker.GetString( frame.name );
|
||||
std::cout << "patching '" << nameStr << "' of '" << imagePath
|
||||
<< "' -> '" << symbolEntry.name << "'" << std::endl;
|
||||
}
|
||||
|
||||
frame.name = AddSymbolString( worker, symbolEntry.name );
|
||||
const char* newName = worker.GetString( frame.name );
|
||||
|
||||
if( symbolEntry.file.length() )
|
||||
{
|
||||
frame.file = AddSymbolString( worker, symbolEntry.file );
|
||||
frame.line = symbolEntry.line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PatchSymbols( tracy::Worker& worker, const std::vector<std::string>& pathSubstitutionsStrings, bool verbose )
|
||||
{
|
||||
std::cout << "Resolving and patching symbols..." << std::endl;
|
||||
|
||||
PathSubstitutionList pathSubstitutionList;
|
||||
for ( const std::string& pathSubst : pathSubstitutionsStrings )
|
||||
{
|
||||
std::size_t pos = pathSubst.find(';');
|
||||
if ( pos == std::string::npos )
|
||||
{
|
||||
std::cerr << "Ignoring invalid path substitution: '" << pathSubst
|
||||
<< " '(please separate the regex of the string to replace with a ';')" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::regex reg(pathSubst.substr(0, pos));
|
||||
std::string replacementStr(pathSubst.substr(pos + 1));
|
||||
pathSubstitutionList.push_back(std::pair(reg, replacementStr));
|
||||
}
|
||||
catch ( std::exception& e )
|
||||
{
|
||||
std::cerr << "Ignoring invalid path substitution: '" << pathSubst
|
||||
<< "' (" << e.what() << ")" << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !PatchSymbolsWithRegex(worker, pathSubstitutionList, verbose) )
|
||||
{
|
||||
std::cerr << "Failed to patch symbols" << std::endl;
|
||||
}
|
||||
}
|
40
third_party/tracy/update/src/OfflineSymbolResolver.h
vendored
Normal file
40
third_party/tracy/update/src/OfflineSymbolResolver.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef __SYMBOLRESOLVER_HPP__
|
||||
#define __SYMBOLRESOLVER_HPP__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <regex>
|
||||
#include <cstdint>
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
struct CallstackFrame;
|
||||
class Worker;
|
||||
}
|
||||
|
||||
struct FrameEntry
|
||||
{
|
||||
tracy::CallstackFrame* frame = nullptr;
|
||||
uint64_t symbolOffset = 0;
|
||||
};
|
||||
|
||||
using FrameEntryList = std::vector<FrameEntry>;
|
||||
|
||||
struct SymbolEntry
|
||||
{
|
||||
std::string name;
|
||||
std::string file;
|
||||
int line = 0;
|
||||
};
|
||||
|
||||
using SymbolEntryList = std::vector<SymbolEntry>;
|
||||
|
||||
bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries );
|
||||
|
||||
void PatchSymbols( tracy::Worker& worker, const std::vector<std::string>& pathSubstitutionsStrings, bool verbose = false );
|
||||
|
||||
using PathSubstitutionList = std::vector<std::pair<std::regex, std::string> >;
|
||||
bool PatchSymbolsWithRegex( tracy::Worker& worker, const PathSubstitutionList& pathSubstituionlist, bool verbose = false );
|
||||
|
||||
#endif // __SYMBOLRESOLVER_HPP__
|
159
third_party/tracy/update/src/OfflineSymbolResolverAddr2Line.cpp
vendored
Normal file
159
third_party/tracy/update/src/OfflineSymbolResolverAddr2Line.cpp
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "OfflineSymbolResolver.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <stdio.h>
|
||||
|
||||
std::string ExecShellCommand( const char* cmd )
|
||||
{
|
||||
std::array<char, 128> buffer;
|
||||
std::string result;
|
||||
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
|
||||
if( !pipe )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
while( fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr )
|
||||
{
|
||||
result += buffer.data();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class SymbolResolver
|
||||
{
|
||||
public:
|
||||
SymbolResolver()
|
||||
{
|
||||
std::stringstream result( ExecShellCommand("which addr2line") );
|
||||
std::getline(result, m_addr2LinePath);
|
||||
|
||||
if( !m_addr2LinePath.length() )
|
||||
{
|
||||
std::cerr << "'addr2line' was not found in the system, please installed it" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Using 'addr2line' found at: '" << m_addr2LinePath.c_str() << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static void escapeShellParam(std::string const& s, std::string& out)
|
||||
{
|
||||
out.reserve( s.size() + 2 );
|
||||
out.push_back( '"' );
|
||||
for( unsigned char c : s )
|
||||
{
|
||||
if( ' ' <= c and c <= '~' and c != '\\' and c != '"' )
|
||||
{
|
||||
out.push_back( c );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.push_back( '\\' );
|
||||
switch( c )
|
||||
{
|
||||
case '"': out.push_back( '"' ); break;
|
||||
case '\\': out.push_back( '\\' ); break;
|
||||
case '\t': out.push_back( 't' ); break;
|
||||
case '\r': out.push_back( 'r' ); break;
|
||||
case '\n': out.push_back( 'n' ); break;
|
||||
default:
|
||||
char const* const hexdig = "0123456789ABCDEF";
|
||||
out.push_back( 'x' );
|
||||
out.push_back( hexdig[c >> 4] );
|
||||
out.push_back( hexdig[c & 0xF] );
|
||||
}
|
||||
}
|
||||
}
|
||||
out.push_back( '"' );
|
||||
}
|
||||
|
||||
bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
{
|
||||
if( !m_addr2LinePath.length() ) return false;
|
||||
|
||||
std:: string escapedPath;
|
||||
escapeShellParam( imagePath, escapedPath );
|
||||
|
||||
size_t entryIdx = 0;
|
||||
while( entryIdx < inputEntryList.size() )
|
||||
{
|
||||
const size_t startIdx = entryIdx;
|
||||
const size_t batchEndIdx = std::min( inputEntryList.size(), startIdx + (size_t)1024 );
|
||||
|
||||
printf( "Resolving symbols [%zu-%zu]\n", startIdx, batchEndIdx );
|
||||
|
||||
// generate a single addr2line cmd line for all addresses in one invocation
|
||||
std::stringstream ss;
|
||||
ss << m_addr2LinePath << " -C -f -e " << escapedPath << " -a ";
|
||||
for( ; entryIdx < batchEndIdx; entryIdx++ )
|
||||
{
|
||||
const FrameEntry& entry = inputEntryList[entryIdx];
|
||||
ss << " 0x" << std::hex << entry.symbolOffset;
|
||||
}
|
||||
|
||||
std::string resultStr = ExecShellCommand( ss.str().c_str() );
|
||||
std::stringstream result( resultStr );
|
||||
|
||||
//printf("executing: '%s' got '%s'\n", ss.str().c_str(), result.str().c_str());
|
||||
|
||||
// The output is 2 lines per entry with the following contents:
|
||||
// hex_address_of_symbol
|
||||
// symbol_name
|
||||
// file:line
|
||||
|
||||
for( size_t i = startIdx ;i < batchEndIdx; i++ )
|
||||
{
|
||||
const FrameEntry& inputEntry = inputEntryList[i];
|
||||
|
||||
SymbolEntry newEntry;
|
||||
|
||||
std::string addr;
|
||||
std::getline( result, addr );
|
||||
std::getline( result, newEntry.name );
|
||||
if( newEntry.name == "??" )
|
||||
{
|
||||
newEntry.name = "[unknown] + " + std::to_string( inputEntry.symbolOffset );
|
||||
}
|
||||
|
||||
std::string fileLine;
|
||||
std::getline( result, fileLine );
|
||||
if( fileLine != "??:?" )
|
||||
{
|
||||
size_t pos = fileLine.find_last_of( ':' );
|
||||
if( pos != std::string::npos )
|
||||
{
|
||||
newEntry.file = fileLine.substr( 0, pos );
|
||||
std::string lineStr = fileLine.substr( pos + 1 );
|
||||
char* after = nullptr;
|
||||
newEntry.line = strtol( lineStr.c_str(), &after, 10 );
|
||||
}
|
||||
}
|
||||
|
||||
resolvedEntries.push_back( std::move( newEntry ) );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_addr2LinePath;
|
||||
};
|
||||
|
||||
bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
{
|
||||
static SymbolResolver symbolResolver;
|
||||
return symbolResolver.ResolveSymbols( imagePath, inputEntryList, resolvedEntries );
|
||||
}
|
||||
|
||||
#endif // #ifndef _WIN32
|
132
third_party/tracy/update/src/OfflineSymbolResolverDbgHelper.cpp
vendored
Normal file
132
third_party/tracy/update/src/OfflineSymbolResolverDbgHelper.cpp
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
|
||||
#include "OfflineSymbolResolver.h"
|
||||
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
class SymbolResolver
|
||||
{
|
||||
public:
|
||||
SymbolResolver()
|
||||
{
|
||||
m_procHandle = GetCurrentProcess();
|
||||
|
||||
if( !SymInitialize(m_procHandle, NULL, FALSE) )
|
||||
{
|
||||
std::cerr << "SymInitialize() failed with: " << GetLastErrorString() << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
const DWORD symopts = SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES;
|
||||
SymSetOptions( symopts );
|
||||
m_dbgHelpInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
~SymbolResolver()
|
||||
{
|
||||
SymCleanup( m_procHandle );
|
||||
}
|
||||
|
||||
bool ResolveSymbolsForModule( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
{
|
||||
if( !m_dbgHelpInitialized ) return false;
|
||||
|
||||
ULONG64 moduleBase = SymLoadModuleEx( m_procHandle, NULL, imagePath.c_str(), NULL, 0, 0, NULL, 0 );
|
||||
if( !moduleBase )
|
||||
{
|
||||
std::cerr << "SymLoadModuleEx() failed for module " << imagePath
|
||||
<< ": " << GetLastErrorString() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < inputEntryList.size(); ++i )
|
||||
{
|
||||
uint64_t offset = inputEntryList[i].symbolOffset;
|
||||
DWORD64 address = moduleBase + offset;
|
||||
|
||||
SYMBOL_INFO* symbolInfo = (SYMBOL_INFO*)s_symbolResolutionBuffer;
|
||||
symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbolInfo->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
SymbolEntry newEntry;
|
||||
|
||||
if( SymFromAddr( m_procHandle, address, NULL, symbolInfo ) )
|
||||
{
|
||||
newEntry.name = symbolInfo->Name;
|
||||
//std::cout << "Resolved symbol to: '" << newEntry.name << "'" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
newEntry.name = "[unknown] + " + std::to_string(offset);
|
||||
}
|
||||
|
||||
IMAGEHLP_LINE lineInfo = { 0 };
|
||||
lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
DWORD displaceMent = 0;
|
||||
if ( SymGetLineFromAddr64( m_procHandle, address, &displaceMent, &lineInfo ) )
|
||||
{
|
||||
newEntry.file = lineInfo.FileName;
|
||||
newEntry.line = int(lineInfo.LineNumber);
|
||||
///std::cout << "\tline_file: " lineInfo.FileName << ":" << int(lineInfo.LineNumber) << std::endl;
|
||||
}
|
||||
|
||||
resolvedEntries.push_back(std::move(newEntry));
|
||||
}
|
||||
|
||||
SymUnloadModule64( m_procHandle, moduleBase );
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t symbolResolutionBufferSize = sizeof(SYMBOL_INFO) + MAX_SYM_NAME;
|
||||
static char s_symbolResolutionBuffer[symbolResolutionBufferSize];
|
||||
|
||||
std::string GetLastErrorString()
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
if (error == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
LPSTR messageBuffer = nullptr;
|
||||
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||
size_t size = FormatMessageA( dwFlags, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR)&messageBuffer, 0, NULL );
|
||||
|
||||
std::string message(messageBuffer, size);
|
||||
LocalFree(messageBuffer);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
bool m_dbgHelpInitialized = false;
|
||||
HANDLE m_procHandle = nullptr;
|
||||
};
|
||||
|
||||
char SymbolResolver::s_symbolResolutionBuffer[symbolResolutionBufferSize];
|
||||
|
||||
bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
{
|
||||
static SymbolResolver resolver;
|
||||
return resolver.ResolveSymbolsForModule( imagePath, inputEntryList, resolvedEntries );
|
||||
}
|
||||
|
||||
#endif // #ifdef _WIN32
|
239
third_party/tracy/update/src/update.cpp
vendored
Normal file
239
third_party/tracy/update/src/update.cpp
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <zstd.h>
|
||||
|
||||
#include "../../public/common/TracyVersion.hpp"
|
||||
#include "../../server/TracyFileRead.hpp"
|
||||
#include "../../server/TracyFileWrite.hpp"
|
||||
#include "../../server/TracyPrint.hpp"
|
||||
#include "../../server/TracyWorker.hpp"
|
||||
#include "../../getopt/getopt.h"
|
||||
|
||||
#include "OfflineSymbolResolver.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
# define ftello64(x) ftello(x)
|
||||
#elif defined _WIN32
|
||||
# define ftello64(x) _ftelli64(x)
|
||||
#endif
|
||||
|
||||
void Usage()
|
||||
{
|
||||
printf( "Usage: update [options] input.tracy output.tracy\n\n" );
|
||||
printf( " -4: enable LZ4 compression\n" );
|
||||
printf( " -h: enable LZ4HC compression\n" );
|
||||
printf( " -e: enable extreme LZ4HC compression (very slow)\n" );
|
||||
printf( " -z level: use Zstd compression with given compression level\n" );
|
||||
printf( " -d: build dictionary for frame images\n" );
|
||||
printf( " -s flags: strip selected data from capture:\n" );
|
||||
printf( " l: locks, m: messages, p: plots, M: memory, i: frame images\n" );
|
||||
printf( " c: context switches, s: sampling data, C: symbol code, S: source cache\n" );
|
||||
printf( " -c: scan for source files missing in cache and add if found\n" );
|
||||
printf( " -r: resolve symbols and patch callstack frames\n");
|
||||
printf( " -p: substitute symbol resolution path with an alternative: \"REGEX_MATCH;REPLACEMENT\"\n");
|
||||
printf( " -j: number of threads to use for compression (-1 to use all cores)\n" );
|
||||
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if( !AttachConsole( ATTACH_PARENT_PROCESS ) )
|
||||
{
|
||||
AllocConsole();
|
||||
SetConsoleMode( GetStdHandle( STD_OUTPUT_HANDLE ), 0x07 );
|
||||
}
|
||||
#endif
|
||||
|
||||
tracy::FileCompression clev = tracy::FileCompression::Zstd;
|
||||
uint32_t events = tracy::EventType::All;
|
||||
int zstdLevel = 3;
|
||||
int streams = 4;
|
||||
bool buildDict = false;
|
||||
bool cacheSource = false;
|
||||
bool resolveSymbols = false;
|
||||
std::vector<std::string> pathSubstitutions;
|
||||
|
||||
int c;
|
||||
while( ( c = getopt( argc, argv, "4hez:ds:crp:j:" ) ) != -1 )
|
||||
{
|
||||
switch( c )
|
||||
{
|
||||
case '4':
|
||||
clev = tracy::FileCompression::Fast;
|
||||
break;
|
||||
case 'h':
|
||||
clev = tracy::FileCompression::Slow;
|
||||
break;
|
||||
case 'e':
|
||||
clev = tracy::FileCompression::Extreme;
|
||||
break;
|
||||
case 'z':
|
||||
clev = tracy::FileCompression::Zstd;
|
||||
zstdLevel = atoi( optarg );
|
||||
if( zstdLevel > ZSTD_maxCLevel() || zstdLevel < ZSTD_minCLevel() )
|
||||
{
|
||||
printf( "Available Zstd compression levels range: %i - %i\n", ZSTD_minCLevel(), ZSTD_maxCLevel() );
|
||||
exit( 1 );
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
buildDict = true;
|
||||
break;
|
||||
case 's':
|
||||
{
|
||||
auto ptr = optarg;
|
||||
do
|
||||
{
|
||||
switch( *optarg )
|
||||
{
|
||||
case 'l':
|
||||
events &= ~tracy::EventType::Locks;
|
||||
break;
|
||||
case 'm':
|
||||
events &= ~tracy::EventType::Messages;
|
||||
break;
|
||||
case 'p':
|
||||
events &= ~tracy::EventType::Plots;
|
||||
break;
|
||||
case 'M':
|
||||
events &= ~tracy::EventType::Memory;
|
||||
break;
|
||||
case 'i':
|
||||
events &= ~tracy::EventType::FrameImages;
|
||||
break;
|
||||
case 'c':
|
||||
events &= ~tracy::EventType::ContextSwitches;
|
||||
break;
|
||||
case 's':
|
||||
events &= ~tracy::EventType::Samples;
|
||||
break;
|
||||
case 'C':
|
||||
events &= ~tracy::EventType::SymbolCode;
|
||||
break;
|
||||
case 'S':
|
||||
events &= ~tracy::EventType::SourceCache;
|
||||
break;
|
||||
default:
|
||||
Usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while( *++optarg != '\0' );
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
cacheSource = true;
|
||||
break;
|
||||
case 'r':
|
||||
resolveSymbols = true;
|
||||
break;
|
||||
case 'p':
|
||||
pathSubstitutions.push_back(optarg);
|
||||
break;
|
||||
case 'j':
|
||||
streams = atoi( optarg );
|
||||
break;
|
||||
default:
|
||||
Usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc != optind + 2) Usage();
|
||||
|
||||
const char* input = argv[optind];
|
||||
const char* output = argv[optind+1];
|
||||
|
||||
printf( "Loading...\r" );
|
||||
fflush( stdout );
|
||||
auto f = std::unique_ptr<tracy::FileRead>( tracy::FileRead::Open( input ) );
|
||||
if( !f )
|
||||
{
|
||||
fprintf( stderr, "Cannot open input file!\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int64_t tLoad, tSave;
|
||||
float ratio;
|
||||
int inVer;
|
||||
{
|
||||
const auto t0 = std::chrono::high_resolution_clock::now();
|
||||
const bool allowBgThreads = false;
|
||||
const bool allowStringModification = resolveSymbols;
|
||||
tracy::Worker worker( *f, (tracy::EventType::Type)events, allowBgThreads, allowStringModification );
|
||||
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
while( !worker.AreSourceLocationZonesReady() ) std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
|
||||
#endif
|
||||
|
||||
const auto t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if( cacheSource ) worker.CacheSourceFiles();
|
||||
if( resolveSymbols ) PatchSymbols( worker, pathSubstitutions );
|
||||
|
||||
auto w = std::unique_ptr<tracy::FileWrite>( tracy::FileWrite::Open( output, clev, zstdLevel, streams ) );
|
||||
if( !w )
|
||||
{
|
||||
fprintf( stderr, "Cannot open output file!\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
printf( "Saving... \r" );
|
||||
fflush( stdout );
|
||||
worker.Write( *w, buildDict );
|
||||
w->Finish();
|
||||
const auto t2 = std::chrono::high_resolution_clock::now();
|
||||
const auto stats = w->GetCompressionStatistics();
|
||||
ratio = 100.f * stats.second / stats.first;
|
||||
inVer = worker.GetTraceVersion();
|
||||
tLoad = std::chrono::duration_cast<std::chrono::nanoseconds>( t1 - t0 ).count();
|
||||
tSave = std::chrono::duration_cast<std::chrono::nanoseconds>( t2 - t1 ).count();
|
||||
}
|
||||
|
||||
FILE* in = fopen( input, "rb" );
|
||||
fseek( in, 0, SEEK_END );
|
||||
const auto inSize = ftello64( in );
|
||||
fclose( in );
|
||||
|
||||
FILE* out = fopen( output, "rb" );
|
||||
fseek( out, 0, SEEK_END );
|
||||
const auto outSize = ftello64( out );
|
||||
fclose( out );
|
||||
|
||||
printf( "%s (%i.%i.%i) {%s} -> %s (%i.%i.%i) {%s, %.2f%%} %s load, %s save, %.2f%% change\n",
|
||||
input, inVer >> 16, ( inVer >> 8 ) & 0xFF, inVer & 0xFF, tracy::MemSizeToString( inSize ),
|
||||
output, tracy::Version::Major, tracy::Version::Minor, tracy::Version::Patch, tracy::MemSizeToString( outSize ), ratio,
|
||||
tracy::TimeToString( tLoad ), tracy::TimeToString( tSave ), float( outSize ) / inSize * 100 );
|
||||
}
|
||||
catch( const tracy::UnsupportedVersion& e )
|
||||
{
|
||||
fprintf( stderr, "The file you are trying to open is from the future version.\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
catch( const tracy::NotTracyDump& e )
|
||||
{
|
||||
fprintf( stderr, "The file you are trying to open is not a tracy dump.\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
catch( const tracy::FileReadError& e )
|
||||
{
|
||||
fprintf( stderr, "The file you are trying to open cannot be mapped to memory.\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
catch( const tracy::LegacyVersion& e )
|
||||
{
|
||||
fprintf( stderr, "The file you are trying to open is from a legacy version.\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user