0

I am working on an application that should compile and debug C# code on the fly.

A simplified version of the code is included below.

What should be changed in this code to run the generated method step by step and get the state of the variables x and y after each step?

If everything should be changed that is okay, I am happy with any constructive response.

EDIT: to clarify: what I want to do is have my code debug the code that is generated with reflection, not the debug function in Visual Studio.

string code = 
@"
namespace MyNameSpace
{
    public class MyClass
    {
        public static int MyMethod()
        {
            var x = 3;
            var y = 4;
            return x * y;
        }
    }
}";

string namespaceName = "MyNameSpace";
string className = "MyClass";
string methodName = "MyMethod";
string language = "csharp";
string classFullname = namespaceName + "." + className;

CodeDomProvider provider = CodeDomProvider.CreateProvider(language);
CompilerParameters parameters = new CompilerParameters();
CompilerResults results;

parameters.OutputAssembly = "Compiler";

parameters.CompilerOptions = "/t:library";
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = false;
parameters.IncludeDebugInformation = true;

results = provider.CompileAssemblyFromSource(parameters, code);

if (results.Errors.Count != 0)
{
    throw new Exception("Code compilation errors occurred.");
}

var instance = results.CompiledAssembly.CreateInstance(classFullname, false);

// TODO run the method step by step and get the state after each step 
Martien de Jong
  • 731
  • 1
  • 7
  • 19
  • *"and debug C# code"* - that's pretty hard and broad I'd say. – Sinatr Nov 17 '17 at 13:03
  • How about putting a [`Debugger.Break();` call](https://learn.microsoft.com/de-de/dotnet/api/system.diagnostics.debugger.break) in your generated code and see whether it is hit and you can step further from there? – Uwe Keim Nov 17 '17 at 13:07
  • Seems like duplicate of [this question](https://stackoverflow.com/q/3977728/1997232). – Sinatr Nov 17 '17 at 13:44
  • I don't think this is possible. A debugger is a separate process that communicates with the debugee via OS services. The debugee must be compiled with debug information pointing to the original source and generate a proper pdb companion. Perhaps you are looking to interpret the C# code instead? – elchido Nov 20 '17 at 15:34
  • @elchido I do have the source and the pdb-file in this case. I can also do something with the opcodes using System.Reflection.Emit. But I don't know how to get to human readable debugger information from there. – Martien de Jong Nov 21 '17 at 08:36
  • 1
    @MartiendeJong This page [https://www.codeguru.com/cpp/v-s/debug/debuggers/article.php/c16451/Writing-a-basic-Windows-Debugger--Part-1.htm] can give you an idea of what's involved in writing a debugger. – elchido Nov 21 '17 at 15:42
  • @elchido thanks, this does offer some insights. So I guess I have to adjust the source code before I compile it so that it sends debugging events after every statement. And I am still puzzled about how to get the values of the variables on each stap without parsing the code myself. – Martien de Jong Nov 22 '17 at 10:04
  • @MartiendeJong It is a task indeed for a debugger to interact with the code to be debugged (setting arbitrary breakpoints, getting values, etc). Debuggers for .Net languages leverage the debugging engine in VS. I don't know what's the scope of your project or how complex the C# you need to compile is, but you may be better off writing an interpreter. – elchido Nov 22 '17 at 15:54
  • @elchido thank you for your efforts and explanation. The code that is used covers about the complete .NET stack, but mostly database operations and things like making zip-files and sending email. So I'm afraid making an interpreter will be equally difficult. – Martien de Jong Nov 24 '17 at 12:08

4 Answers4

0

This configuration may help you:

parameters.GenerateInMemory = false; //default
parameters.TempFiles = new 
TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true);
parameters.IncludeDebugInformation = true;
parameters.TempFiles.KeepFiles = true
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
mostafa
  • 261
  • 1
  • 2
  • 10
  • What is different from the OP's question and _why_ may it help? – Uwe Keim Nov 17 '17 at 13:08
  • debugging is possible if you have pdb file, by the above settings you tell code generator keep pdb file and don't optimize generated file – mostafa Nov 17 '17 at 13:15
  • There is exact [duplicate](https://stackoverflow.com/a/1797139/1997232) of this answer. – Sinatr Nov 17 '17 at 13:42
  • The difference with my code is that this includes an instruction to keep temp (PDB?) files. I assumed that since I compiled to inmemory that the PDB is also saved inmemory. Do you know if this is the case or not? Thanks for the answer. – Martien de Jong Nov 17 '17 at 15:37
  • @Sinatr this is not a duplicate of the question you mentioned because I want to retrieve debug info at runtime, not debug in Visual Studio. – Martien de Jong Nov 17 '17 at 15:38
  • @MartiendeJong, I mention duplicate of *answer*, not question. This answer. The code is exact and [this smells](https://meta.stackoverflow.com/q/268629/1997232). – Sinatr Nov 17 '17 at 15:46
  • @Sinatr these 4 lines of codes are used in other places as well. I am not sure what this means since this is exactly the code that you would use to configure the parameters in this way so it is only logical that it appears in any place where it is needed. He could have done some attribution though. My stake is to get answers to my question and this helped me so in that regard I thank mostafa. – Martien de Jong Nov 20 '17 at 09:26
0

To debug the generated code you will need the pdb files. To have those while debugging your application, just have to tell the compiler where to save the temporary files. For this you can just add the following line to your parameters:

parameters.TempFiles = new TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true);

You can then step into the Invokation of your targeted method. The Code could look like this:

var method = instance?.GetType().GetMethod(methodName);
method?.Invoke(instance, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture);

If you want the Debugger to automatically stop, when entering your "MyMethod", you can modify your string likes this:

string code =
@"using System.Diagnostics;

namespace MyNameSpace
{
    public class MyClass
    {
        public int MyMethod()
        {
            Debugger.Break();
            var x = 3;
            var y = 4;
            return x * y;
        }
    }
}";
Milster
  • 652
  • 4
  • 13
0

Elchido pointed out in the comments that maybe I should look for an interpreter. After a bit of searching I came across CSI: A Simple C# Interpreter.

https://www.codeproject.com/Articles/10212/CSI-A-Simple-C-Interpreter

After investigating, my conclusion is that it is possible to use either and interpreter or the Codedom compiler to create debugger-like functionality, but it takes a significant effort.

The solution that I am working on involves splitting the code into separate statements and put all variables in an array.

The 'MyMethod' function is split into parts:

public static object Line1()
{
    return 3;
}

public static object Line2()
{
    return 4;
}

public static object Line3(object x, object y)
{
    return x*y;
}

After compiling the code using Codedom compiler, I can do the following:

Dictionary<string, object> vars = new Dictionary<string, object>();

List<MethodInfo> lines = new List<MethodInfo>();
lines.Add(type.GetMethod("Line1"));
lines.Add(type.GetMethod("Line2"));
lines.Add(type.GetMethod("Line3"));

vars["x"] = lines[0].Invoke(instance, new object[] { });
vars["y"] = lines[1].Invoke(instance, new object[] { });
vars["@return"] = lines[2].Invoke(instance, new object[] { vars["x"], vars["y"] });

Note that this is not a working solution yet, a lot of work still has to be done to convert the 'MyMethod code' into separate lines and extract the variables. I will post an update when I have more/better code.

Martien de Jong
  • 731
  • 1
  • 7
  • 19
-5

Click just left side of your code it will mark red dot that is called break point.After that when your code execute at the point it will break at the point and you can debug step by step bt pressing F10 key.

spender
  • 117,338
  • 33
  • 229
  • 351