Click here to Skip to main content
15,884,473 members
Articles / Programming Languages / C# 6.0

Simple DebugInterface

Rate me:
Please Sign up or sign in to vote.
4.56/5 (3 votes)
9 Jul 2017CPOL4 min read 4.5K   1  
A simple overview of my opensource library allowing developers to add a dynamic interface to get values from the properties of object instances in other processes

Introduction

Have you ever suffered from a situation where your ability to debug is getting in the way of your ability to run your application or service successfully? There are many situations in a multi-tier based product where an element somewhere in the chain of command is on a short leash - it has seconds or milliseconds to respond to a request or the calling process will fault because it timed out waiting for a response. The set of libraries I will present in this article will allow one scenario that will help to avoid such issues.

Using the Code

Firstly, the code described in this article is open sourced under a BSD license, and is available from GitHub.

https://github.com/memsom/DebugInterface

The components can also be added to your solution via NuGet. For the server component (you add this to the code you want to gain debug information from):

From the package manager console command line:

C++
Install-Package Ratcow.Debugging.Server

Or search for Ratcow.Debugging.Server in the Package manager in Visual Studio. For the client (you should create a host app to view the data): From the package manager console command line

C++
Install-Package Ratcow.Debugging.Client

Or search for Ratcow.Debugging.Client in the Package manager in Visual Studio. We are using the latest versions in this article. These are "0.0.1" for the client and "0.0.2" for the server. But the interface is extremely simple, so it is unlikely that any breaking changes will be added.

In your app, you must register some instances to make them available, and when registering those instances, you will provide the names of one or more properties. For example, given this class:

C#
public class TestClassOuter
{
    public TestClassOuter()
    {
        Value = new TestClass();
    }
    public TestClass Value{get; set;}
}
public class TestClass
{
    public string S { get; set; }
    public int I { get; set; }
}

We can see that the outer class "TestClassOuter" contains a property of the type of the inner, and the inner "TestClass" has two properties. This example shows the two possible ways you can register values.

C#
            var c = new TestClass
            {
                I = 10,
                S = "hello"
            };
 
            var dbs = new DebugInterface();
 
            dbs.RegisterProperties(
               c,        //The instance
               "c",      //The name we are registering (this can be anything, 
                         //it is simply what we report to attached clients)
               "I", "S"  //A list of all the properties we want to present the value of 
                         //to the clients connected.
            );

You could easily also do this:

C#
var c = new TestOuterClass();
c.Value.I = 10;
c.Value.S = "hello";

var dbs = new DebugInterface();

dbs.RegisterProperties(c, "c", "Value");

At this point, we have a valid DebugInterface. We can register this with the WCF interface and start debugging the values. In your application, it will be much the same. To do this, add a private variable to your application (or use your favorite IoC container). This instance must exist throughout the lifetime of your application.

C#
  ServiceHost debugInterfaceService;

....

  debugInterfaceService = Ratcow.Debugging.Server.DebugInterface.Start(dbs);

At this point, your application will now be exporting the properties you registered through the WCF service embedded in the library, so long as you have added the WCF configuration to your app.config. (This step will go away with a future release, it's just here as I released the libraries early to use them in a project I'm working on.)

XML
<system.serviceModel>
  <services>
    <!-- simple generic debug interface -->
    <service behaviorConfiguration="DebugInterfaceBehave"
                name="Ratcow.Debugging.Server.DebugInterface">
      <endpoint address="http://127.0.0.1:9001/DebugInterface"
                binding="basicHttpBinding"
                contract="Ratcow.Debugging.Server.IDebugInterface" />
      <host>
        <baseAddresses>
          <add baseAddress="http://127.0.0.1:9001/DebugInterface" />
        </baseAddresses>
      </host>
    </service>
  </services>

  <behaviors>
    <serviceBehaviors>
      <behavior name="DebugInterfaceBehave">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="true" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

Now that you have an app that is running - verify that the service is active. Go to:

http://127.0.0.1:9001/DebugInterface

On your host machine. You should see the overview of the service. If you don't, you might need to look at firewall access and also could need to run your app with administrator privileges (just to get off the ground.) You can alter the permissions on the app to allow the WCF service to run later - but to test it is running successfully, we will need to cut a few corners here.

Your client can be as complex as you want it to be. But a basic client is included in the GitHub repo, and is reproduced below:

C#
class Program
{
    static void Main(string[] args)
    {
        var client = new DebugInterfaceClient("http://127.0.0.1:9001/DebugInterface");

        var names = client.GetVariableNames();

        foreach (var name in names)
        {
            Console.Write(name);
            var value = client.GetVariableValue(name);
            Console.WriteLine($" : {value}");
        }

        Console.WriteLine("\r\n----------------------");
        Console.WriteLine("any key to exit");
        Console.ReadLine();
    }
}

The important thing here:

  1. Instantiate the client with the URL to the server component. If this is on another machine, you'll need to use the IP address or DNS name of that machine.
  2. Calling "GetVariableNames()" on the client will get you a complete list of registered names in a string array.
  3. Calling "GetVariableValue()" passing a valid name received from the call to "GetVariableNames()" will get you the current value of that variable in real time. The library will convert the data to JSON, so you can then process that data as needed. JSON is very useful in this application, because it is neutral and will serialize any value without really caring if the class instance can be deserialized at the other end. It is also language agnostic.

Really, that is it!! You have a live view of your variables, and the debugger is never involved in slowing/stopping your application's progress. I use this technique so frequently, that I created this fairly generic library to enable others to use the same technique.

As with all debug interfaces - you will probably want to exclude this code from production builds.

This library is still evolving, and I will keep updating this article when more changes happen.

Points of Interest

This is a whirlwind overview - I'll add more detail as I improve the library.

History

  • 2017-07-09: Created the basic article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --