Runtime Flow API provides programmatic access to saved Runtime Flow monitoring data in a .rfd file from a standalone application, real time access to function calls from an extension dll, pause and resume monitoring. RuntimeFlowExt.dll .NET 4.0 library includes all API methods and definitions, you can find it in the portable package and in the installation directory (c:\Program Files (x86)\Runtime Flow).
Download sample code demonstrating data access: RFClassGraph_10.zip. (It uses Microsoft Automatic Graph Layout (MSAGL) library to display graph data.)
RFClassGraph project is a standalone application that shows class relationship diagram from a .rfd file. You need to copy MSAGL dlls into the RFClassGraph.exe directory and pass the file path as a command line parameter: RFClassGraph.exe m:\temp\rfp.rfd
private void LoadFlowData(string path) { RuntimeFlowExt.FlowData flow = RF.Utils.Load(path); // Functions in the flow tree are stored as ids. // summary.functions give function information by id: foreach (RuntimeFlowExt.KeyValuePair<uint, RuntimeFlowExt.FunctionInfo> i in flow.summary.functions) functions[i.Key] = i.Value; // Visit each function call in each thread in the flow tree: foreach (RuntimeFlowExt.ThreadNode t in flow.flow) { foreach (RuntimeFlowExt.FlowNode f in t.children) LoadFlowData(f); } } private void LoadFlowData(RuntimeFlowExt.FlowNode node) { foreach (RuntimeFlowExt.FlowNode f in node.children) { graph.AddEdge(FindClassName(node), FindClassName(f)); LoadFlowData(f); } } private string FindClassName(RuntimeFlowExt.FlowNode node) { return functions[node.functionID].className; }
RFClassGraphExt project is a Runtime Flow extension dll. You need to place RFClassGraphExt.dll (with the msagl directory) in the Extensions directory in the portable files directory or in the Runtime Flow installation directory (c:\Program Files (x86)\Runtime Flow). It creates a window that shows the same class relationship diagram as above, but now in real time as monitoring is running.
Extension class implements the required RuntimeFlowExt.IExtension interface:namespace RuntimeFlowExt { public interface IExtension { void Init(IEngine engine); void Close(); } }
class Extension : RuntimeFlowExt.IExtension { public void Init(RuntimeFlowExt.IEngine engine) { window.Show(); // Open the class graph window threads = new System.Collections.Generic.Dictionary(); // Keeps call stack for each thread engine.FunctionEnter += OnFunctionEnter; engine.FunctionLeave += OnFunctionLeave; engine.FunctionsReset += OnFunctionsReset; // Called when starting or resuming monitoring engine.Clear += OnClear; // Called when Clear on the Runtime Flow toolbar is clicked } public void Close() { engine.FunctionEnter -= OnFunctionEnter; engine.FunctionLeave -= OnFunctionLeave; engine.FunctionsReset -= OnFunctionsReset; engine.Clear -= OnClear; window.Close(); } private void OnFunctionEnter(uint threadID, uint functionID, uint paramsID) { if (!threads.ContainsKey(threadID)) threads.Add(threadID, new Thread()); Thread thread = threads[threadID]; uint? currentFunction = thread.GetCurrentFunction(); if (currentFunction.HasValue) AddEdge(currentFunction.Value, functionID); thread.Enter(functionID); } private void OnFunctionLeave(uint threadID, uint functionID, uint returnValueID) { threads[threadID].Leave(functionID); } private void OnFunctionsReset() { threads.Clear(); window.Clear(); } private void OnClear() { window.Clear(); } private void AddEdge(uint sourceFunctionID, uint targetFunctionID) { string source = FindClassName(sourceFunctionID); string target = FindClassName(targetFunctionID); window.AddEdge(source, target); } private string FindClassName(uint functionID) { return engine.GetFunctionInfo(functionID).className; } MainWindow window; RuntimeFlowExt.IEngine engine; System.Collections.Generic.Dictionary threads; }
Download sample code: RFShortcuts_10.zip.
The RFShortcuts extension registers two global Windows shortcuts Win+F11 and Win+F12 to resume and pause monitoring. It allows you quickly pause and resume monitoring without switching from the monitored application:class Extension : RuntimeFlowExt.IExtension { public Extension() { hotKeyResume = new HotKey(System.Windows.Input.Key.F11, KeyModifier.Win, OnResume, false); hotKeyPause = new HotKey(System.Windows.Input.Key.F12, KeyModifier.Win, OnPause, false); } public void Init(RuntimeFlowExt.IEngine engine_) { engine = engine_; hotKeyPause.Register(); hotKeyResume.Register(); } public void Close() { hotKeyPause.Unregister(); hotKeyResume.Unregister(); } private void OnPause(HotKey hotKey) { engine.PauseMonitoring(); } private void OnResume(HotKey hotKey) { engine.ResumeMonitoring(); } private RuntimeFlowExt.IEngine engine; private HotKey hotKeyPause, hotKeyResume; }
Download sample code: RFDtoTXT_10.zip.
The RFDtoTXT command line utility extract the full method calls sequence from a .rfd file and saves it in a plaint text file.
Usage: RFDtoTXT.exe log.rfd log.txt
Thread 12372 void App.Main() void App..ctor() void App.InitializeComponent() void MainWindow..ctor() void MainWindow.InitializeComponent() void MainWindow.System.Windows.Markup.IComponentConnector.Connect(0, MainWindow{_contentLoaded=true})...(2, Button) (dup=3) void MainWindow.System.Windows.Markup.IComponentConnector.Connect(0, MainWindow{_contentLoaded=true}) void MainWindow.Button_Click(Button, RoutedEventArgs) void MainWindow.Button_Click_1(Button, RoutedEventArgs)Main sample code is shown below:
public void Convert() { foreach (RuntimeFlowExt.ThreadNode t in flow.flow) { outputFile.WriteLine(formatter.ThreadName(t)); foreach (RuntimeFlowExt.FlowNode f in t.children) ConvertFlow(f, 1); } } private void ConvertFlow(RuntimeFlowExt.FlowNode node, int offset) { outputFile.WriteLine(new string(' ', offset) + formatter.FunctionCall(node)); foreach (RuntimeFlowExt.FlowNode f in node.children) ConvertFlow(f, offset + 1); }
Copyright 2008 - 2025 Vlasov Studio (Best in class Visual Studio extensions and tools)