mirror of
https://github.com/wolfpld/tracy.git
synced 2024-11-26 07:54:36 +00:00
Add support for symbol path replacement using regex in tracy-edit
This commit is contained in:
parent
fe0e5f3358
commit
0491cad49a
@ -11,6 +11,19 @@
|
|||||||
|
|
||||||
#include "OfflineSymbolResolver.h"
|
#include "OfflineSymbolResolver.h"
|
||||||
|
|
||||||
|
bool ApplyPathSubstitutions(std::string& path, const PathSubstitutionList& pathSubstituionlist)
|
||||||
|
{
|
||||||
|
for( const auto& substituion : pathSubstituionlist )
|
||||||
|
{
|
||||||
|
if( std::regex_match(path, substituion.first) )
|
||||||
|
{
|
||||||
|
path = std::regex_replace( path, substituion.first, substituion.second );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: use string hash map to reduce duplication or use some worker string internal hashing
|
// TODO: use string hash map to reduce duplication or use some worker string internal hashing
|
||||||
tracy::StringIdx AddSymbolString(tracy::Worker& worker, const char* str)
|
tracy::StringIdx AddSymbolString(tracy::Worker& worker, const char* str)
|
||||||
{
|
{
|
||||||
@ -18,7 +31,8 @@ tracy::StringIdx AddSymbolString(tracy::Worker& worker, const char* str)
|
|||||||
return tracy::StringIdx( newStringIdx );
|
return tracy::StringIdx( newStringIdx );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PatchSymbols(SymbolResolver* resolver, tracy::Worker& worker, bool verbose)
|
bool PatchSymbols(SymbolResolver* resolver, tracy::Worker& worker,
|
||||||
|
const PathSubstitutionList& pathSubstituionlist, bool verbose)
|
||||||
{
|
{
|
||||||
if( !resolver )
|
if( !resolver )
|
||||||
{
|
{
|
||||||
@ -72,23 +86,29 @@ bool PatchSymbols(SymbolResolver* resolver, tracy::Worker& worker, bool verbose)
|
|||||||
imageItEnd = entriesPerImageIdx.end(); imageIt != imageItEnd; ++imageIt )
|
imageItEnd = entriesPerImageIdx.end(); imageIt != imageItEnd; ++imageIt )
|
||||||
{
|
{
|
||||||
tracy::StringIdx imageIdx( imageIt->first );
|
tracy::StringIdx imageIdx( imageIt->first );
|
||||||
const char* imageName = worker.GetString( imageIdx );
|
std::string imagePath = worker.GetString( imageIdx );
|
||||||
|
|
||||||
FrameEntryList& entries = imageIt->second;
|
FrameEntryList& entries = imageIt->second;
|
||||||
|
|
||||||
std::cout << "Resolving " << entries.size() << " symbols for image: '" << imageName << "'" << std::endl;
|
|
||||||
|
|
||||||
if (!entries.size())
|
if (!entries.size())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "Resolving " << entries.size() << " symbols for image: '"
|
||||||
|
<< imagePath << "'" << std::endl;
|
||||||
|
const bool substituted = ApplyPathSubstitutions(imagePath, pathSubstituionlist);
|
||||||
|
if (substituted)
|
||||||
|
{
|
||||||
|
std::cout << "\tPath substituted to: '" << imagePath << "'" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
SymbolEntryList resolvedEntries;
|
SymbolEntryList resolvedEntries;
|
||||||
ResolveSymbols( resolver, imageName, entries, resolvedEntries );
|
ResolveSymbols( resolver, imagePath, entries, resolvedEntries );
|
||||||
|
|
||||||
if( resolvedEntries.size() != entries.size() )
|
if( resolvedEntries.size() != entries.size() )
|
||||||
{
|
{
|
||||||
std::cerr << "ERROR: failed to resolve all entries! (got: " << resolvedEntries.size() << ")" << std::endl;
|
std::cerr << " failed to resolve all entries! (got: "
|
||||||
|
<< resolvedEntries.size() << ")" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,12 +120,15 @@ bool PatchSymbols(SymbolResolver* resolver, tracy::Worker& worker, bool verbose)
|
|||||||
|
|
||||||
tracy::CallstackFrame& frame = *frameEntry.frame;
|
tracy::CallstackFrame& frame = *frameEntry.frame;
|
||||||
if (!symbolEntry.name.length())
|
if (!symbolEntry.name.length())
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if( verbose )
|
if( verbose )
|
||||||
{
|
{
|
||||||
const char* nameStr = worker.GetString( frame.name );
|
const char* nameStr = worker.GetString( frame.name );
|
||||||
std::cout << "patching '" << nameStr << "' of '" << imageName << "' -> '" << symbolEntry.name << "'" << std::endl;
|
std::cout << "patching '" << nameStr << "' of '" << imagePath
|
||||||
|
<< "' -> '" << symbolEntry.name << "'" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.name = AddSymbolString( worker, symbolEntry.name.c_str() );
|
frame.name = AddSymbolString( worker, symbolEntry.name.c_str() );
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
namespace tracy
|
namespace tracy
|
||||||
{
|
{
|
||||||
@ -32,10 +33,13 @@ struct SymbolEntry
|
|||||||
|
|
||||||
using SymbolEntryList = std::vector<SymbolEntry>;
|
using SymbolEntryList = std::vector<SymbolEntry>;
|
||||||
|
|
||||||
bool ResolveSymbols(SymbolResolver* resolver, const char* imageName,
|
bool ResolveSymbols(SymbolResolver* resolver, const std::string& imagePath,
|
||||||
const FrameEntryList& inputEntryList,
|
const FrameEntryList& inputEntryList,
|
||||||
SymbolEntryList& resolvedEntries);
|
SymbolEntryList& resolvedEntries);
|
||||||
|
|
||||||
bool PatchSymbols(SymbolResolver* resolver, tracy::Worker& worker, bool verbose = false);
|
using PathSubstitutionList = std::vector<std::pair<std::regex, std::string> >;
|
||||||
|
|
||||||
|
bool PatchSymbols(SymbolResolver* resolver, tracy::Worker& worker,
|
||||||
|
const PathSubstitutionList& pathSubstituionlist, bool verbose = false);
|
||||||
|
|
||||||
#endif // __SYMBOLRESOLVER_HPP__
|
#endif // __SYMBOLRESOLVER_HPP__
|
@ -33,12 +33,12 @@ public:
|
|||||||
: m_addr2LinePath(addr2linePath)
|
: m_addr2LinePath(addr2linePath)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool ResolveSymbols(const char* imageName, const FrameEntryList& inputEntryList,
|
bool ResolveSymbols(const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||||
SymbolEntryList& resolvedEntries)
|
SymbolEntryList& resolvedEntries)
|
||||||
{
|
{
|
||||||
// generate a single addr2line cmd line for all addresses in one invocation
|
// generate a single addr2line cmd line for all addresses in one invocation
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << m_addr2LinePath << " -C -f -e " << imageName << " -a ";
|
ss << m_addr2LinePath << " -C -f -e " << imagePath << " -a ";
|
||||||
for ( const FrameEntry& entry : inputEntryList )
|
for ( const FrameEntry& entry : inputEntryList )
|
||||||
{
|
{
|
||||||
ss << " 0x" << std::hex << entry.symbolOffset;
|
ss << " 0x" << std::hex << entry.symbolOffset;
|
||||||
@ -111,12 +111,12 @@ void DestroySymbolResolver(SymbolResolver* resolver)
|
|||||||
delete resolver;
|
delete resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResolveSymbols(SymbolResolver* resolver, const char* imageName,
|
bool ResolveSymbols(SymbolResolver* resolver, const std::string& imagePath,
|
||||||
const FrameEntryList& inputEntryList, SymbolEntryList& resolvedEntries)
|
const FrameEntryList& inputEntryList, SymbolEntryList& resolvedEntries)
|
||||||
{
|
{
|
||||||
if (resolver)
|
if (resolver)
|
||||||
{
|
{
|
||||||
return resolver->ResolveSymbols(imageName, inputEntryList, resolvedEntries);
|
return resolver->ResolveSymbols( imagePath, inputEntryList, resolvedEntries );
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -39,13 +39,13 @@ public:
|
|||||||
SymCleanup( m_procHandle );
|
SymCleanup( m_procHandle );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResolveSymbolsForModule(const char* fileName, const FrameEntryList& inputEntryList,
|
bool ResolveSymbolsForModule(const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||||
SymbolEntryList& resolvedEntries)
|
SymbolEntryList& resolvedEntries)
|
||||||
{
|
{
|
||||||
ULONG64 moduleBase = SymLoadModuleEx( m_procHandle, NULL, fileName, NULL, 0, 0, NULL, 0 );
|
ULONG64 moduleBase = SymLoadModuleEx( m_procHandle, NULL, imagePath.c_str(), NULL, 0, 0, NULL, 0);
|
||||||
if (!moduleBase)
|
if (!moduleBase)
|
||||||
{
|
{
|
||||||
std::cerr << "SymLoadModuleEx() failed for module " << fileName
|
std::cerr << "SymLoadModuleEx() failed for module " << imagePath
|
||||||
<< ": " << GetLastErrorString() << std::endl;
|
<< ": " << GetLastErrorString() << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -128,13 +128,13 @@ void DestroySymbolResolver(SymbolResolver* resolver)
|
|||||||
delete resolver;
|
delete resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResolveSymbols(SymbolResolver* resolver, const char* imageName,
|
bool ResolveSymbols(SymbolResolver* resolver, const std::string& imagePath,
|
||||||
const FrameEntryList& inputEntryList,
|
const FrameEntryList& inputEntryList,
|
||||||
SymbolEntryList& resolvedEntries)
|
SymbolEntryList& resolvedEntries)
|
||||||
{
|
{
|
||||||
if( resolver )
|
if( resolver )
|
||||||
{
|
{
|
||||||
return resolver->ResolveSymbolsForModule( imageName, inputEntryList, resolvedEntries );
|
return resolver->ResolveSymbolsForModule( imagePath, inputEntryList, resolvedEntries );
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <regex>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -22,6 +23,7 @@ struct Args
|
|||||||
const char* outputTracyPath = nullptr;
|
const char* outputTracyPath = nullptr;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
bool resolveSymbols = false;
|
bool resolveSymbols = false;
|
||||||
|
std::vector<std::string> pathSubstitutions;
|
||||||
tracy::FileWrite::Compression compressionType = tracy::FileWrite::Compression::Zstd;
|
tracy::FileWrite::Compression compressionType = tracy::FileWrite::Compression::Zstd;
|
||||||
int compressionLevel = 5;
|
int compressionLevel = 5;
|
||||||
};
|
};
|
||||||
@ -30,13 +32,14 @@ void PrintUsageAndExit()
|
|||||||
{
|
{
|
||||||
std::cerr << "Modify a tracy file" << std::endl;
|
std::cerr << "Modify a tracy file" << std::endl;
|
||||||
std::cerr << "Usage:" << std::endl;
|
std::cerr << "Usage:" << std::endl;
|
||||||
std::cerr << " extract [OPTION...] <input trace file> <output tracy file>" << std::endl;
|
std::cerr << " tracy-edit [OPTION...] <input trace file> <output tracy file>" << std::endl;
|
||||||
std::cerr << std::endl;
|
std::cerr << std::endl;
|
||||||
std::cerr << " -h, --help Print usage" << std::endl;
|
std::cerr << " -h, --help Print usage" << std::endl;
|
||||||
std::cerr << " -v, --verbose Enable verbose logging" << std::endl;
|
std::cerr << " -v, --verbose Enable verbose logging" << std::endl;
|
||||||
std::cerr << " -r, --resolveSymbols Resolve symbols and patch callstack frames" << std::endl;
|
std::cerr << " -r, --resolveSymbols Resolve symbols and patch callstack frames" << std::endl;
|
||||||
std::cerr << " -c, --compression arg Compress output with the given compression algo" << std::endl;
|
std::cerr << " -s, --substitutePath \"REGEX_MATCH;REPLACEMENT\" Substitute symbol resolution path with an alternative" << std::endl;
|
||||||
std::cerr << " -l, --compressesionLevel arg Level of compression" << std::endl;
|
std::cerr << " -c, --compression Type Set the compression algorithm used for the output [Fast,Slow,Extreme,Zstd]" << std::endl;
|
||||||
|
std::cerr << " -l, --compressesionLevel Level Level of compression" << std::endl;
|
||||||
exit( 1 );
|
exit( 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,13 +77,14 @@ Args ParseArgs( int argc, char** argv )
|
|||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ "verbose", no_argument, NULL, 'v' },
|
{ "verbose", no_argument, NULL, 'v' },
|
||||||
{ "resolveSymbols", no_argument, NULL, 'r' },
|
{ "resolveSymbols", no_argument, NULL, 'r' },
|
||||||
|
{ "substitutePath", required_argument, NULL, 's' },
|
||||||
{ "compression", required_argument, NULL, 'c' },
|
{ "compression", required_argument, NULL, 'c' },
|
||||||
{ "compressesionLevel", required_argument, NULL, 'l' },
|
{ "compressesionLevel", required_argument, NULL, 'l' },
|
||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ( (c = getopt_long( argc, argv, "hvrc:l:", long_opts, NULL )) != -1 )
|
while ( (c = getopt_long( argc, argv, "hvrc:l:s:", long_opts, NULL )) != -1 )
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
@ -96,6 +100,9 @@ Args ParseArgs( int argc, char** argv )
|
|||||||
case 'c':
|
case 'c':
|
||||||
args.compressionType = getCompressionFromString( optarg );
|
args.compressionType = getCompressionFromString( optarg );
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
args.pathSubstitutions.push_back( optarg );
|
||||||
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
args.compressionLevel = atoi( optarg );
|
args.compressionLevel = atoi( optarg );
|
||||||
break;
|
break;
|
||||||
@ -116,6 +123,8 @@ Args ParseArgs( int argc, char** argv )
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PatchSymbols(tracy::Worker& worker, const Args& args);
|
||||||
|
|
||||||
int main( int argc, char** argv )
|
int main( int argc, char** argv )
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -144,21 +153,9 @@ int main( int argc, char** argv )
|
|||||||
|
|
||||||
std::cout << "Loaded." << std::endl;
|
std::cout << "Loaded." << std::endl;
|
||||||
|
|
||||||
// attempt to resolve symbols only if requested
|
|
||||||
if(args.resolveSymbols)
|
if(args.resolveSymbols)
|
||||||
{
|
{
|
||||||
std::cout << "Resolving and patching symbols..." << std::endl;
|
PatchSymbols(worker, args);
|
||||||
|
|
||||||
SymbolResolver* resolver = CreateResolver();
|
|
||||||
if(!resolver)
|
|
||||||
{
|
|
||||||
std::cerr << "Failed to create symbol resolver - skipping resolving" << std::endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PatchSymbols(resolver, worker);
|
|
||||||
DestroySymbolResolver(resolver);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// save out capture file with new compression options
|
// save out capture file with new compression options
|
||||||
@ -179,3 +176,45 @@ int main( int argc, char** argv )
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PatchSymbols(tracy::Worker& worker, const Args& args)
|
||||||
|
{
|
||||||
|
std::cout << "Resolving and patching symbols..." << std::endl;
|
||||||
|
|
||||||
|
PathSubstitutionList pathSubstitutionList;
|
||||||
|
for (const std::string& pathSubst : args.pathSubstitutions)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolResolver* resolver = CreateResolver();
|
||||||
|
if (resolver)
|
||||||
|
{
|
||||||
|
PatchSymbols(resolver, worker, pathSubstitutionList, args.verbose);
|
||||||
|
|
||||||
|
DestroySymbolResolver(resolver);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to create symbol resolver - skipping resolving" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user