Add tool for getting symbol parent calls.

This commit is contained in:
Bartosz Taudul
2026-05-20 01:20:03 +02:00
parent 55c5348fad
commit 4bbfe5edbd
4 changed files with 116 additions and 0 deletions

View File

@@ -200,5 +200,26 @@
"required": ["address"]
}
}
},
{
"type": "function",
"function": {
"name": "symbol_parents",
"description": "Get the top N entry call stacks for a given symbol address. Shows the call paths through which execution reached this function.",
"parameters": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Symbol address, for example `0x12345678`."
},
"limit": {
"type": "integer",
"description": "Maximum number of call stacks to return. Default: 10."
}
},
"required": ["address"]
}
}
}
]

View File

@@ -147,6 +147,18 @@ std::string TracyLlmChat::ToolCallDescription( const nlohmann::json& json ) cons
if( sym->isInline ) return "";
return "Disassemble symbol: " + std::string( m_worker.GetString( sym->name ) );
}
else if( name == "symbol_parents" )
{
if( !args.contains( "address" ) ) return "";
auto addr = args["address"].get_ref<const std::string&>();
auto symAddr = strtoull( addr.c_str(), nullptr, 16 );
auto sym = m_worker.GetSymbolData( symAddr );
if( !sym ) return "";
if( sym->isInline ) return "";
std::string limit;
if( args.contains( "limit" ) ) limit = ", limit: " + std::to_string( args["limit"].get<uint32_t>() );
return "Symbol parents: " + std::string( m_worker.GetString( sym->name ) ) + limit;
}
return "";
}

View File

@@ -8,6 +8,7 @@
#include <tidybuffio.h>
#include <time.h>
#include <regex>
#include <vector>
#include "TracyConfig.hpp"
#include "TracyDisassembly.hpp"
@@ -15,9 +16,12 @@
#include "TracyLlmApi.hpp"
#include "TracyLlmTools.hpp"
#include "TracyManualData.hpp"
#include "TracyPrint.hpp"
#include "TracyStorage.hpp"
#include "TracyUtility.hpp"
#include "TracyView.hpp"
#include "TracyWorker.hpp"
#include "tracy_pdqsort.h"
constexpr const char* NoNetworkAccess = "Internet access is disabled by the user. Inform the user that they may enable it in the settings, so that you can use the tools to gather information.";
@@ -194,6 +198,12 @@ std::string TracyLlmTools::HandleToolCalls( const std::string& tool, const nlohm
{
return SymbolDisasm( Param( "address" ) );
}
#ifndef TRACY_NO_STATISTICS
else if( tool == "symbol_parents" )
{
return SymbolParents( Param( "address" ), ParamOptU32( "limit", 10 ) );
}
#endif
return "Unknown tool call: " + tool;
}
catch( const std::exception& e )
@@ -1028,4 +1038,74 @@ std::string TracyLlmTools::SymbolDisasm( const std::string& address ) const
return json.dump( -1, ' ', false, nlohmann::json::error_handler_t::replace );
}
#ifndef TRACY_NO_STATISTICS
std::string TracyLlmTools::SymbolParents( const std::string& address, uint32_t limit ) const
{
uint64_t symAddr = strtoull( address.c_str(), nullptr, 16 );
auto ss = m_worker.GetSymbolStats( symAddr );
if( !ss ) return "No parent callstack data for this symbol.";
const auto symbol = m_worker.GetSymbolData( symAddr );
if( !symbol ) return "Symbol not found.";
if( symbol->isInline ) return "Symbol is inline.";
auto stats = ss->parents;
auto excl = ss->excl;
const auto symLen = symbol->size.Val();
auto inSym = m_worker.GetInlineSymbolList( symAddr, symLen );
if( inSym )
{
const auto symEnd = symAddr + symLen;
while( *inSym < symEnd )
{
auto istat = m_worker.GetSymbolStats( *inSym++ );
if( !istat ) continue;
excl += istat->excl;
for( auto& v : istat->baseParents )
{
auto it = stats.find( v.first );
if( it == stats.end() )
{
stats[v.first] = v.second;
}
else
{
it->second += v.second;
}
}
}
}
if( stats.empty() ) return "No parent callstack data for this symbol.";
std::vector<decltype(stats.begin())> sorted;
sorted.reserve( stats.size() );
for( auto it = stats.begin(); it != stats.end(); ++it ) sorted.push_back( it );
pdqsort_branchless( sorted.begin(), sorted.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second > rhs->second; } );
if( sorted.size() > limit ) sorted.resize( limit );
nlohmann::json result = {
{ "entries", nlohmann::json::array() },
{ "hint", "Frame N is where frame N-1 returns to. The caller of frame N-1 may differ from frame N." }
};
auto& entries = result["entries"];
for( auto& entry : sorted )
{
auto& cs = m_worker.GetParentCallstack( entry->first );
auto frames = m_view.GetCallstackJson( cs.data(), cs.size() )["frames"];
char buf[32];
auto end = PrintFloat( buf, buf+32, 100.f * entry->second / excl, 4 );
*end = '\0';
entries.push_back( {
{ "callstack", frames },
{ "percentage", buf }
} );
}
return result.dump( -1, ' ', false, nlohmann::json::error_handler_t::replace );
}
#endif
}

View File

@@ -66,6 +66,9 @@ private:
std::string SourceSearch( std::string query, bool caseInsensitive, const std::string& path ) const;
std::string GetSkill( const std::string& name ) const;
std::string SymbolDisasm( const std::string& address ) const;
#ifndef TRACY_NO_STATISTICS
std::string SymbolParents( const std::string& address, uint32_t limit ) const;
#endif
void ManualEmbeddingsWorker( TracyLlmApi& api );