mirror of
https://github.com/wolfpld/tracy.git
synced 2026-08-29 14:38:21 +00:00
Add LLM tool for listing sampling statistics.
This commit is contained in:
@@ -221,5 +221,26 @@
|
||||
"required": ["address"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "sampling_stats",
|
||||
"description": "Get sampling statistics for functions, sorted by time spent in each.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "ECMAScript regex to search for."
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum number of entries to return. Default: 30."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -159,6 +159,13 @@ std::string TracyLlmChat::ToolCallDescription( const nlohmann::json& json ) cons
|
||||
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;
|
||||
}
|
||||
else if( name == "sampling_stats" )
|
||||
{
|
||||
std::string query, limit;
|
||||
if( args.contains( "query" ) ) query = ", query: " + args["query"].get_ref<const std::string&>();
|
||||
if( args.contains( "limit" ) ) limit = ", limit: " + std::to_string( args["limit"].get<uint32_t>() );
|
||||
return "Sampling stats" + query + limit;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <algorithm>
|
||||
#include <curl/curl.h>
|
||||
#include <inttypes.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <libbase64.h>
|
||||
#include <pugixml.hpp>
|
||||
@@ -203,6 +204,11 @@ std::string TracyLlmTools::HandleToolCalls( const std::string& tool, const nlohm
|
||||
{
|
||||
return SymbolParents( Param( "address" ), ParamOptU32( "limit", 10 ) );
|
||||
}
|
||||
else if( tool == "sampling_stats" )
|
||||
{
|
||||
std::string empty;
|
||||
return SamplingStats( ParamOptString( "query", empty ), ParamOptU32( "limit", 30 ) );
|
||||
}
|
||||
#endif
|
||||
return "Unknown tool call: " + tool;
|
||||
}
|
||||
@@ -1106,6 +1112,112 @@ std::string TracyLlmTools::SymbolParents( const std::string& address, uint32_t l
|
||||
}
|
||||
return result.dump( -1, ' ', false, nlohmann::json::error_handler_t::replace );
|
||||
}
|
||||
|
||||
std::string TracyLlmTools::SamplingStats( const std::string& query, uint32_t limit ) const
|
||||
{
|
||||
if( !m_worker.AreSymbolSamplesReady() ) return "Sampling data is not ready yet. Wait for background processing to complete.";
|
||||
if( m_worker.GetCallstackSampleCount() == 0 ) return "No call stack samples in this trace.";
|
||||
|
||||
std::regex rx;
|
||||
if( !query.empty() )
|
||||
{
|
||||
try
|
||||
{
|
||||
rx = std::regex( query );
|
||||
}
|
||||
catch( const std::regex_error& e )
|
||||
{
|
||||
return "Error: Invalid query regex: " + std::string( e.what() );
|
||||
}
|
||||
}
|
||||
|
||||
const auto& symMap = m_worker.GetSymbolMap();
|
||||
const auto& symStat = m_worker.GetSymbolStats();
|
||||
|
||||
struct SymEntry
|
||||
{
|
||||
uint64_t symAddr;
|
||||
uint32_t excl;
|
||||
};
|
||||
|
||||
std::vector<SymEntry> data;
|
||||
data.reserve( symStat.size() );
|
||||
for( auto& v : symStat )
|
||||
{
|
||||
auto sit = symMap.find( v.first );
|
||||
if( sit == symMap.end() ) continue;
|
||||
data.emplace_back( v.first, v.second.excl );
|
||||
}
|
||||
if( data.empty() ) return "No symbol statistics available.";
|
||||
|
||||
unordered_flat_map<uint64_t, SymEntry> baseMap;
|
||||
for( auto& v : data )
|
||||
{
|
||||
auto sym = m_worker.GetSymbolData( v.symAddr );
|
||||
const auto symAddr = ( sym && sym->isInline ) ? m_worker.GetSymbolForAddress( v.symAddr ) : v.symAddr;
|
||||
auto it = baseMap.find( symAddr );
|
||||
if( it == baseMap.end() )
|
||||
{
|
||||
baseMap.emplace( symAddr, SymEntry { symAddr, v.excl } );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( symAddr == it->second.symAddr );
|
||||
it->second.excl += v.excl;
|
||||
}
|
||||
}
|
||||
|
||||
data.clear();
|
||||
for( auto& v : baseMap )
|
||||
{
|
||||
auto sit = symMap.find( v.second.symAddr );
|
||||
if( sit == symMap.end() ) continue;
|
||||
if( !query.empty() )
|
||||
{
|
||||
const auto name = m_worker.GetString( sit->second.name );
|
||||
if( !std::regex_search( name, rx ) ) continue;
|
||||
}
|
||||
data.emplace_back( v.second );
|
||||
}
|
||||
if( data.empty() ) return "No symbols match the query.";
|
||||
|
||||
pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l.excl > r.excl; } );
|
||||
if( data.size() > limit ) data.resize( limit );
|
||||
|
||||
const auto period = m_worker.GetSamplingPeriod();
|
||||
const auto totalSamples = m_worker.GetCallstackSampleCount();
|
||||
|
||||
nlohmann::json result = {
|
||||
{ "total_time", TimeToString( totalSamples * period ) },
|
||||
{ "entries", nlohmann::json::array() },
|
||||
{ "hint", "Entries are sorted by exclusive time (child time not included), highest first." }
|
||||
};
|
||||
auto& entries = result["entries"];
|
||||
|
||||
for( auto& v : data )
|
||||
{
|
||||
auto sit = symMap.find( v.symAddr );
|
||||
assert( sit != symMap.end() );
|
||||
|
||||
char addr[32];
|
||||
snprintf( addr, sizeof( addr ), "0x%" PRIx64, v.symAddr );
|
||||
|
||||
const auto file = m_worker.GetString( sit->second.file );
|
||||
char loc[1024];
|
||||
snprintf( loc, sizeof( loc ), "%s:%u", file, sit->second.line );
|
||||
|
||||
entries.push_back( {
|
||||
{ "name", m_worker.GetString( sit->second.name ) },
|
||||
{ "address", addr },
|
||||
{ "location", loc },
|
||||
{ "image", m_worker.GetString( sit->second.imageName ) },
|
||||
{ "time", TimeToString( v.excl * period ) },
|
||||
{ "code_size", MemSizeToString( sit->second.size.Val() ) }
|
||||
} );
|
||||
}
|
||||
|
||||
return result.dump( -1, ' ', false, nlohmann::json::error_handler_t::replace );
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ private:
|
||||
std::string SymbolDisasm( const std::string& address ) const;
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
std::string SymbolParents( const std::string& address, uint32_t limit ) const;
|
||||
std::string SamplingStats( const std::string& query, uint32_t limit ) const;
|
||||
#endif
|
||||
|
||||
void ManualEmbeddingsWorker( TracyLlmApi& api );
|
||||
|
||||
Reference in New Issue
Block a user