Home Screencasts Quick Start Case Studies API Download Order Blog*

Runtime Flow Runtime Flow

API

Introduction

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 solution

Reading saved 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

RFClassGraph
Main sample code is shown below:
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;
}

Real time access

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;
}

Pause and resume monitoring

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;
}

Save flow in a text file

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

Simple flow
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);
}

Timing

Runtime Flow is not designed for profiling and doesn't collect timing information. Nevertheless, from an extension in FunctionEnter and FunctionLeave handlers you can record current time and display it along with method call information.

Sample RFTiming extension opens a separate RFTiming window and in run time shows method calls from a monitored application along with a time stamp and an internal thread id. As you can see, with light load it correctly shows timer interval as 100 ms:

Simple flow Simple flow

RFTiming also flattens threads so you can troubleshot thread interactions seeing actual time line of method calls:

Simple flow Simple flow

Main sample code is shown below:

public void Init(RuntimeFlowExt.IEngine engine_)
{
    window = new MainWindow();
    window.Show();
    formatter = new Formatter(engine_);

    threads = new System.Collections.Generic.Dictionary();
    engine = engine_;
    engine.FunctionEnter += OnFunctionEnter;
    engine.FunctionLeave += OnFunctionLeave;
    engine.FunctionsReset += OnFunctionsReset;
    engine.Clear += OnClear;
}

private void OnFunctionEnter(uint threadID, uint functionID, uint paramsID)
{
    System.DateTime currentTime = System.DateTime.Now;
    if (!threads.ContainsKey(threadID))
        threads.Add(threadID, new Thread());

    Thread thread = threads[threadID];
    window.AddLine(formatter.FormatFunctionCall(currentTime, threadID, functionID, paramsID, thread.GetCurrentStackDepth()));
    thread.Enter(functionID);
}