Accessing at runtime nested method's parameters values

M

Milan

Hi all,

I have some C# code with nested method call and I would like during runtime
to log on different level the values of method parameters (just like VS
offers Debug-> Windows->Call Stack). Is it possible because StackTrace class
does not seems to offer support of accessing parameters values, it support
just paremeter types and names ?

best regards,
Milan.
 
A

Arne Vajhøj

I have some C# code with nested method call and I would like during runtime
to log on different level the values of method parameters (just like VS
offers Debug-> Windows->Call Stack). Is it possible because StackTrace class
does not seems to offer support of accessing parameters values, it support
just paremeter types and names ?

I don't think that this information is available for code running
outside the debugger.

Arne
 
P

Peter Duniho

Milan said:
Thanks Arne,

but what the others thinks ?

To a first approximation, Arne's answer is correct. Why would we add
anything to that?

Of course, with some hacking you can always get at the function
arguments and local variables. In the end, it's all just memory. It's
obviously possible, because the debugger does it. But just like Arne,
I'm not aware of any API built into .NET to provide that information.

If it's really important to you, you should look into debugger
implementations for .NET. I haven't written a debugger for .NET, so
unfortunately I don't have any specific knowledge there for you.

Pete
 
A

Arne Vajhøj

To a first approximation, Arne's answer is correct. Why would we add
anything to that?

Of course, with some hacking you can always get at the function
arguments and local variables. In the end, it's all just memory. It's
obviously possible, because the debugger does it. But just like Arne,
I'm not aware of any API built into .NET to provide that information.

After the JIT compiler has inlined some code, keept certain
variables in registers etc. then it is very difficult problem
to solve.

Arne
 
A

Arne Vajhøj

I don't think that this information is available for code running
outside the debugger.

If one really needs this info and is willing to add
developer hours and runtime overhead to get a solution,
then there is the swiss-army-knife-for-stuff-not-possible-in-C#
solution: use an AOP weaver to modify the code to do as
required.

Below is an example using my favorite tool AspectDNG. It is
a bit long, but it is not a simple problem.

Arne

====================================

C:\e\callfun>type Trace.cs
using System;
using System.Collections.Generic;
using System.Text;

namespace ESup
{
public class Arg
{
private Type typ;
private object val;
public Arg(Type typ, object val)
{
this.typ = typ;
this.val = val;
}
public Type Typ { get { return typ; } }
public object Val { get { return val; } }
public override string ToString()
{
return typ.Name + ":" + (val != null ? val.ToString() :
"null");
}
}
public class Call
{
private string clznam;
private string metnam;
private IList<Arg> args;
public Call(string clznam, string metnam)
{
this.clznam = clznam;
this.metnam = metnam;
this.args = new List<Arg>();
}
public string Clznam { get { return clznam; } }
public string Metnam { get { return metnam; } }
public IList<Arg> Args { get { return args; } }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(clznam);
sb.Append(".");
sb.Append(metnam);
sb.Append(" -");
foreach(Arg a in args)
{
sb.Append(" ");
sb.Append(a.ToString());
}
return sb.ToString();
}
}
public class Trace
{
private static Trace inst = new Trace();
private Stack<Call> stk;
private Trace()
{
stk = new Stack<Call>();
}
public static Trace Instance { get { return inst; } }
public IEnumerable<Call> Stack { get { return stk; } }
public void Push(Call v)
{
stk.Push(v);
}
public void PushName(string clznam, string metnam)
{
Push(new Call(clznam, metnam));
}
public void PushArg(Type typ, object val)
{
stk.Peek().Args.Add(new Arg(typ, val));
}
public void Pop()
{
stk.Pop();
}
}
}

C:\e\callfun>csc /t:library Trace.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.3053
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.


C:\e\callfun>type Test.cs
using System;

using ESup;

namespace E
{
public class Test
{
public void Sub3(int iv, double xv, string sv)
{
Console.WriteLine(iv + " " + xv + " " + (sv ?? "null"));
foreach(Call cal in Trace.Instance.Stack)
{
Console.WriteLine(cal);
}
}
public void Sub2(int iv, double xv)
{
Sub3(iv, xv, "ABC");
Sub3(iv, xv, null);
}
public void Sub1(int iv)
{
Sub2(iv, 123.456);
}
public static void Main(string[] args)
{
Test o = new Test();
o.Sub1(123);
}
}
}
C:\e\callfun>csc /t:exe /r:Trace.dll Test.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.3053
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.


C:\e\callfun>Test
123 123,456 ABC
123 123,456 null

C:\e\callfun>type AOPTrace.cs
using System;

using DotNetGuru.AspectDNG.Joinpoints;

namespace ESup
{
public class CallTracker
{
[AroundCall("* E.*::*(*)")]
public static object SaveCall(JoinPoint jp)
{

if(jp is MethodJoinPoint)
{
MethodJoinPoint mjp = (MethodJoinPoint)jp;

Trace.Instance.PushName(mjp.TargetOperation.DeclaringType.FullName,
mjp.TargetOperation.Name);
for (int i = 0; i < mjp.Arguments.Length; i++)
{

Trace.Instance.PushArg(mjp.TargetOperation.GetParameters().ParameterType,
mjp.Arguments);
}
}
object res = jp.Proceed();
if(jp is MethodJoinPoint)
{
Trace.Instance.Pop();
}
return res;
}
}
}

C:\e\callfun>csc /t:library /r:AspectDNG.exe /r:Trace.dll AOPTrace.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.3053
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.


C:\e\callfun>AspectDNG Test.exe AOPTrace.dll

C:\e\callfun>Test
123 123,456 ABC
E.Test.Sub3 - Int32:123 Double:123,456 String:ABC
E.Test.Sub2 - Int32:123 Double:123,456
E.Test.Sub1 - Int32:123
123 123,456 null
E.Test.Sub3 - Int32:123 Double:123,456 String:null
E.Test.Sub2 - Int32:123 Double:123,456
E.Test.Sub1 - Int32:123
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top