@@ -1703,7 +1703,12 @@ bool EsrevenAdapter::SupportFeature(DebugAdapterCapacity feature)
17031703 case DebugAdapterSupportThreads:
17041704 return true ;
17051705 case DebugAdapterSupportTTD:
1706- return m_canReverseContinue && m_canReverseStep;
1706+ {
1707+ bool ttdSupported = m_canReverseContinue && m_canReverseStep;
1708+ LogInfo (" SupportFeature(DebugAdapterSupportTTD): m_canReverseContinue=%d, m_canReverseStep=%d, returning %d" ,
1709+ m_canReverseContinue, m_canReverseStep, ttdSupported);
1710+ return ttdSupported;
1711+ }
17071712 default :
17081713 return false ;
17091714 }
@@ -2078,10 +2083,265 @@ Ref<Settings> EsrevenAdapterType::RegisterAdapterSettings()
20782083 "readOnly" : false
20792084 })" );
20802085
2086+ settings->RegisterSetting (" ttd.queryTimeout" ,
2087+ R"JSON( {
2088+ "title" : "TTD Query Timeout",
2089+ "type" : "number",
2090+ "default" : 60000,
2091+ "minValue" : 5000,
2092+ "maxValue" : 600000,
2093+ "description" : "Timeout in milliseconds for TTD query operations (calls, memory, events). Increase for large wildcard queries. Default: 60000ms (60s)",
2094+ "readOnly" : false
2095+ })JSON" );
2096+
2097+ settings->RegisterSetting (" ttd.maxCallsQueryResults" ,
2098+ R"JSON( {
2099+ "title" : "Max Calls Query Results",
2100+ "type" : "number",
2101+ "default" : 10000,
2102+ "minValue" : 0,
2103+ "maxValue" : 18446744073709551615,
2104+ "description" : "Maximum number of results to return from TTD Calls queries. Set to 0 for no limit.",
2105+ "readOnly" : false
2106+ })JSON" );
2107+
20812108 return settings;
20822109}
20832110
20842111
2112+ TTDPosition EsrevenAdapter::GetCurrentTTDPosition ()
2113+ {
2114+ if (!m_rspConnector)
2115+ return TTDPosition ();
2116+
2117+ auto reply = m_rspConnector->TransmitAndReceive (
2118+ RspData (" rvn:get-current-transition" ), " ack_then_reply" , nullptr ,
2119+ std::chrono::milliseconds (5000 ));
2120+
2121+ std::string json = reply.AsString ();
2122+
2123+ if (json.empty () || json[0 ] == ' E' )
2124+ return TTDPosition ();
2125+
2126+ // Server returns JSON null when no transition is available
2127+ if (json == " null" || json.empty ())
2128+ return TTDPosition ();
2129+
2130+ // Manual JSON extraction — no external library, same pattern as GetTTDCallsForSymbols
2131+ std::string objectStr = json;
2132+ auto extractUInt64 = [&objectStr](const std::string& fieldName) -> uint64_t {
2133+ std::string searchStr = " \" " + fieldName + " \" :" ;
2134+ size_t pos = objectStr.find (searchStr);
2135+ if (pos == std::string::npos)
2136+ return 0 ;
2137+ pos += searchStr.length ();
2138+ while (pos < objectStr.length () && std::isspace (objectStr[pos]))
2139+ pos++;
2140+ if (objectStr.substr (pos, 4 ) == " null" )
2141+ return 0 ;
2142+ size_t end = pos;
2143+ while (end < objectStr.length () && (std::isdigit (objectStr[end]) || objectStr[end] == ' -' ))
2144+ end++;
2145+ if (end > pos)
2146+ {
2147+ try { return std::stoull (objectStr.substr (pos, end - pos)); } catch (...) {}
2148+ }
2149+ return 0 ;
2150+ };
2151+
2152+ uint64_t transitionId = extractUInt64 (" transition_id" );
2153+ // TTDPosition: sequence = transition_id, step = 0 (REVEN has no sub-step granularity)
2154+ return TTDPosition (transitionId, 0 );
2155+ }
2156+
2157+
2158+ bool EsrevenAdapter::SetTTDPosition (const TTDPosition& position)
2159+ {
2160+ if (!m_rspConnector)
2161+ return false ;
2162+
2163+ DebuggerEvent dbgevt;
2164+ dbgevt.type = ResumeEventType;
2165+ PostDebuggerEvent (dbgevt);
2166+
2167+ InvalidateCache ();
2168+
2169+ auto stopReason = GenericGo (fmt::format (" rvn:set-current-transition:{}" , position.sequence ));
2170+ return stopReason != InternalError;
2171+ }
2172+
2173+
2174+ std::vector<TTDCallEvent> EsrevenAdapter::GetTTDCallsForSymbols (const std::string& symbols, uint64_t startReturnAddress, uint64_t endReturnAddress)
2175+ {
2176+ std::vector<TTDCallEvent> events;
2177+
2178+ if (symbols.empty ())
2179+ {
2180+ LogError (" No symbols provided for TTD calls query" );
2181+ return events;
2182+ }
2183+
2184+ // Get settings
2185+ auto adapterSettings = GetAdapterSettings ();
2186+ BNSettingsScope scope = SettingsResourceScope;
2187+ auto timeoutMs = adapterSettings->Get <uint64_t >(" ttd.queryTimeout" , GetData (), &scope);
2188+ auto maxResults = adapterSettings->Get <uint64_t >(" ttd.maxCallsQueryResults" , GetData (), &scope);
2189+ auto timeout = std::chrono::milliseconds (timeoutMs);
2190+
2191+ LogInfo (" GetTTDCallsForSymbols: symbols='%s', timeout=%lldms, maxResults=%llu" ,
2192+ symbols.c_str (), timeoutMs, maxResults);
2193+
2194+ try
2195+ {
2196+ // Detect wildcard patterns to add max_symbols limit
2197+ bool isWildcard = (symbols.find (' *' ) != std::string::npos) ||
2198+ (symbols.find (' ?' ) != std::string::npos);
2199+
2200+ // Send rvn:get-calls-by-symbol packet with optional return address range and max_symbols
2201+ // Format: rvn:get-calls-by-symbol:<symbol>[:<start_ret_addr>:<end_ret_addr>[:<max_symbols>]]
2202+ std::string packet;
2203+ if (startReturnAddress != 0 || endReturnAddress != 0 )
2204+ {
2205+ // Include return address range for server-side filtering
2206+ packet = fmt::format (" rvn:get-calls-by-symbol:{}:{:x}:{:x}{}" ,
2207+ symbols,
2208+ startReturnAddress != 0 ? startReturnAddress : 0 ,
2209+ endReturnAddress != 0 ? endReturnAddress : 0xFFFFFFFFFFFFFFFF ,
2210+ isWildcard ? " :50" : " " ); // Limit wildcards to 50 symbols
2211+ }
2212+ else
2213+ {
2214+ // No filtering - query all calls
2215+ packet = fmt::format (" rvn:get-calls-by-symbol:{}{}" ,
2216+ symbols,
2217+ isWildcard ? " :::50" : " " ); // Format: symbol:::max_symbols
2218+ }
2219+
2220+ // Send with custom timeout
2221+ auto reply = m_rspConnector->TransmitAndReceive (
2222+ RspData (packet),
2223+ " ack_then_reply" ,
2224+ nullptr ,
2225+ timeout // Use configured timeout
2226+ );
2227+
2228+ // Check for error response
2229+ if (reply.m_data [0 ] == ' E' )
2230+ {
2231+ LogError (" Failed to get calls for symbol: %s" , symbols.c_str ());
2232+ return events;
2233+ }
2234+
2235+ std::string jsonData = reply.AsString ();
2236+ if (jsonData.empty () || jsonData == " []" )
2237+ {
2238+ return events;
2239+ }
2240+
2241+ // Manual JSON parsing (no external library dependency)
2242+ // Expected format: [{"transition_id":...,"function_name":"...","function_address":...,"call_instruction_address":...,"return_address":...,"thread_id":...}, ...]
2243+
2244+ size_t pos = 0 ;
2245+ while ((pos = jsonData.find (' {' , pos)) != std::string::npos)
2246+ {
2247+ size_t endPos = jsonData.find (' }' , pos);
2248+ if (endPos == std::string::npos)
2249+ break ;
2250+
2251+ std::string objectStr = jsonData.substr (pos, endPos - pos + 1 );
2252+
2253+ // Helper lambda to extract uint64 field
2254+ auto extractUInt64 = [&objectStr](const std::string& fieldName) -> uint64_t {
2255+ std::string searchStr = " \" " + fieldName + " \" :" ;
2256+ size_t fieldPos = objectStr.find (searchStr);
2257+ if (fieldPos == std::string::npos)
2258+ return 0 ;
2259+
2260+ fieldPos += searchStr.length ();
2261+ // Skip whitespace
2262+ while (fieldPos < objectStr.length () && std::isspace (objectStr[fieldPos]))
2263+ fieldPos++;
2264+
2265+ // Check for null
2266+ if (objectStr.substr (fieldPos, 4 ) == " null" )
2267+ return 0 ;
2268+
2269+ // Extract number
2270+ size_t endNum = fieldPos;
2271+ while (endNum < objectStr.length () && (std::isdigit (objectStr[endNum]) || objectStr[endNum] == ' -' ))
2272+ endNum++;
2273+
2274+ if (endNum > fieldPos)
2275+ {
2276+ try {
2277+ return std::stoull (objectStr.substr (fieldPos, endNum - fieldPos));
2278+ } catch (...) {
2279+ return 0 ;
2280+ }
2281+ }
2282+ return 0 ;
2283+ };
2284+
2285+ // Helper lambda to extract string field
2286+ auto extractString = [&objectStr](const std::string& fieldName) -> std::string {
2287+ std::string searchStr = " \" " + fieldName + " \" :\" " ;
2288+ size_t fieldPos = objectStr.find (searchStr);
2289+ if (fieldPos == std::string::npos)
2290+ return " " ;
2291+
2292+ fieldPos += searchStr.length ();
2293+ size_t endQuote = objectStr.find (' \" ' , fieldPos);
2294+ if (endQuote == std::string::npos)
2295+ return " " ;
2296+
2297+ return objectStr.substr (fieldPos, endQuote - fieldPos);
2298+ };
2299+
2300+ // Extract fields
2301+ uint64_t transition_id = extractUInt64 (" transition_id" );
2302+ std::string function_name = extractString (" function_name" );
2303+ uint64_t function_address = extractUInt64 (" function_address" );
2304+ uint64_t call_instruction_address = extractUInt64 (" call_instruction_address" );
2305+ uint64_t return_address = extractUInt64 (" return_address" );
2306+ uint64_t thread_id = extractUInt64 (" thread_id" );
2307+
2308+ // Note: Return address filtering is done server-side for performance
2309+
2310+ // Create TTDCallEvent
2311+ TTDCallEvent event;
2312+ event.eventType = " Call" ;
2313+ event.threadId = static_cast <uint32_t >(thread_id);
2314+ event.uniqueThreadId = static_cast <uint32_t >(thread_id);
2315+ event.function = function_name;
2316+ event.functionAddress = function_address;
2317+ event.returnAddress = return_address;
2318+ event.returnValue = 0 ;
2319+ event.hasReturnValue = false ;
2320+ event.timeStart = TTDPosition (transition_id, 0 );
2321+ event.timeEnd = TTDPosition (transition_id, 0 ); // Same as timeStart for call events
2322+
2323+ events.push_back (event);
2324+
2325+ pos = endPos + 1 ;
2326+ }
2327+
2328+ LogInfo (" Retrieved %zu call events for symbol: %s" , events.size (), symbols.c_str ());
2329+
2330+ // Apply client-side result limiting (Option 2 pattern)
2331+ if (maxResults > 0 && events.size () > maxResults)
2332+ {
2333+ LogWarn (" Query returned %zu results, limiting to %llu" , events.size (), maxResults);
2334+ events.resize (maxResults);
2335+ }
2336+ }
2337+ catch (const std::exception& e)
2338+ {
2339+ LogError (" Exception while getting calls for symbol %s: %s" , symbols.c_str (), e.what ());
2340+ }
2341+
2342+ return events;
2343+ }
2344+
20852345Ref<Settings> EsrevenAdapterType::GetAdapterSettings ()
20862346{
20872347 static Ref<Settings> settings = RegisterAdapterSettings ();
0 commit comments