"Debug only" code that should run only when "turned on" - c#

I would like to add some C# "debug only" code that only runs if the person debugging requests it. In C++, I used to do something similar to the following:
void foo()
{
// ...
#ifdef DEBUG
static bool s_bDoDebugOnlyCode = false;
if (s_bDoDebugOnlyCode)
{
// Debug only code here gets executed when the person debugging
// manually sets the bool above to true. It then stays for the rest
// of the session until they set it to false.
}
#endif
// ...
}
I can't do exactly the same in C# since there is no local statics.
Question: What is the best way to accomplish this in C#?
Should I use a private class static field with C# preprocessor directives (#if/#endif DEBUG)?
Should I use the Conditional attribute (to hold the code), and then a private class static field (not surrounded by C# preprocessor directives #if/#endif DEBUG?).
Something else?

An instance variable would probably be the way to do what you want. You could make it static to persist the same value for the life of the program (or thread depending on your static memory model), or make it an ordinary instance var to control it over the life of an object instance. If that instance is a singleton, they'll behave the same way.
#if DEBUG
private /*static*/ bool s_bDoDebugOnlyCode = false;
#endif
void foo()
{
// ...
#if DEBUG
if (s_bDoDebugOnlyCode)
{
// Code here gets executed only when compiled with the DEBUG constant,
// and when the person debugging manually sets the bool above to true.
// It then stays for the rest of the session until they set it to false.
}
#endif
// ...
}
Just to be complete, pragmas (preprocessor directives) are considered a bit of a kludge to use to control program flow. .NET has a built-in answer for half of this problem, using the "Conditional" attribute.
private /*static*/ bool doDebugOnlyCode = false;
[Conditional("DEBUG")]
void foo()
{
// ...
if (doDebugOnlyCode)
{
// Code here gets executed only when compiled with the DEBUG constant,
// and when the person debugging manually sets the bool above to true.
// It then stays for the rest of the session until they set it to false.
}
// ...
}
No pragmas, much cleaner. The downside is that Conditional can only be applied to methods, so you'll have to deal with a boolean variable that doesn't do anything in a release build. As the variable exists solely to be toggled from the VS execution host, and in a release build its value doesn't matter, it's pretty harmless.

What you're looking for is
[ConditionalAttribute("DEBUG")]
attribute.
If you for instance write a method like :
[ConditionalAttribute("DEBUG")]
public static void MyLovelyDebugInfoMethod(string message)
{
Console.WriteLine("This message was brought to you by your debugger : ");
Console.WriteLine(message);
}
any call you make to this method inside your own code will only be executed in debug mode. If you build your project in release mode, even call to the "MyLovelyDebugInfoMethod" will be ignored and dumped out of your binary.
Oh and one more thing if you're trying to determine whether or not your code is currently being debugged at the execution moment, it is also possible to check if the current process is hooked by a JIT. But this is all together another case. Post a comment if this is what you2re trying to do.

You could try this if you only need the code to run when you have a debugger attached to the process.
if (Debugger.IsAttached)
{
// do some stuff here
}

If you want to know whether if debugging, everywhere in program.
Use this.
Declare global variable.
bool isDebug=false;
Create function for checking debug mode
[ConditionalAttribute("DEBUG")]
public static void isDebugging()
{
isDebug = true;
}
In the initialize method call the function
isDebugging();
Now in the entire program. You can check for debugging and do the operations.
Hope this Helps!

I think it may be worth mentioning that [ConditionalAttribute] is in the System.Diagnostics; namespace. I stumbled a bit when I got:
Error 2 The type or namespace name 'ConditionalAttribute' could not be found (are you missing a using directive or an assembly reference?)
after using it for the first time (I thought it would have been in System).

Related

Can't execute statement with VS Debugger Interop

I'm writing a debugger extension VSPackage in which I want to execute a statement in the debugged process when a breakpoint is hit. In my extension code I have this:
void Initialize()
{
// ...standard vspackage init code omitted...
Globals.Init((DTE2)GetService(typeof(DTE)));
Globals.DebuggerEvents.OnEnterBreakMode += (dbgEventReason reason, ref dbgExecutionAction action) =>
{
try
{
var e1 = Globals.Application.Debugger.GetExpression("1+2");
Debug.WriteLine(e1.Value); // Prints "3"
Globals.Application.Debugger.ExecuteStatement("x = 1+2", 1000);
Debug.WriteLine("OK"); // Never prints this
}
catch (Exception ex)
{
Debug.WriteLine("Error: "+ex); // Nor this
}
}
}
When debugging this extension in a VS instance I load a trivial program looking like this
static void Main()
{
int x = 5;
Console.WriteLine("X is "+x); // Breakpoint on this line
}
When the breakpoint is hit in the debugged process the handler is called and the output window for the extension shows "3", so evaluating expressions works, but it never succeeds executing the statement. Nothing more is printed to the output window. No exception or timeout occurs and I can't continue debugging the process, the debugger appears to have crashed.
The globals class just holds the DTE and DebuggerEvents
public static class Globals
{
public static void Init(DTE2 dte)
{
Application = dte;
DebuggerEvents = dte.Events.DebuggerEvents;
}
public static DTE2 Application { get; private set; }
public static DebuggerEvents DebuggerEvents { get; private set; }
}
What am I doing wrong, or misunderstanding here?
This is an old question, but there is so little on Google about these issues, I thought I'd offer some help. Some important considerations:
Use GetExpresssion3(TreatAsStatement:=True), if possible, instead of ExecuteStatement (I could not get ExecuteStatement working properly).
The thread calling your delegate (OnEnterBreakMode) is the same thread that will need will to run again in order to execute your expression or statement. Therefore, call your GetExpression method on a new thread (Task.Run..)
You will have to monitor and manage the Reason value for OnEnterBreakMode. The initial Reason is UnwindFromException for the actual unhandled exception. Then, it is expected you are setting a variable, such as tempStack = New System.Diagnostics.StackTrace(True). OnEnterBreakMode will be called again following execution of this statement, but this time with Evaluation for the Reason. At this point you now call all of your GetExpressions to collect all of your data without additional OnEnterBreakMode calls.
Dim dte2 As EnvDTE80.DTE2 = GetGlobalService(GetType(EnvDTE.DTE))
Dim debugger5 as EnvDTE100.Debugger5 = Dte2.Debugger
Interesting design observation: System.Diagnostics.StackTrace is a very strangely designed class in the context of the rest of the .NET framework until you have to work on this project where you are extracting the StackTrace through this very technique and see the benefit of its otherwise odd design.
I was tinkering a lot with Visual Studio debugging, and the ultimate cause of freezing was always related to thread handling: VS allows any piece of code to run while debugging only in the main thread. Every other thread is disabled and in case your debug code depends on a different thread it will freeze too.
My guess: You initialized your DTE in a different thread than what you are debugging.
Assumed result: Delegate method tries to load the context of the initializing thread which is different from the debugged thread, and thus it is bound to get frozen.
Proposed solution: Don't use delegate method. They implicitly refer back to the original execution context. Instead register a regular method, and reinitialize your DTE in that context.

How to use ApprovalTests on Teamcity?

I am using Approval Tests. On my dev machine I am happy with DiffReporter that starts TortoiseDiff when my test results differ from approved:
[UseReporter(typeof (DiffReporter))]
public class MyApprovalTests
{ ... }
However when the same tests are running on Teamcity and results are different tests fail with the following error:
System.Exception : Unable to launch: tortoisemerge.exe with arguments ...
Error Message: The system cannot find the file specified
---- System.ComponentModel.Win32Exception : The system cannot find the file
specified
Obviously it cannot find tortoisemerge.exe and that is fine because it is not installed on build agent. But what if it gets installed? Then for each fail another instance of tortoisemerge.exe will start and nobody will close it. Eventually tons of tortoisemerge.exe instances will kill our servers :)
So the question is -- how tests should be decorated to run Tortoise Diff on local machine
and just report errors on build server? I am aware of #IF DEBUG [UseReporter(typeof (DiffReporter))] but would prefer another solution if possible.
There are a couple of solutions to the question of Reporters and CI. I will list them all, then point to a better solution, which is not quite enabled yet.
Use the AppConfigReporter. This allows you to set the reporter in your AppConfig, and you can use the QuietReporter for CI.
There is a video here, along with many other reporters. The AppConfigReporter appears at 6:00.
This has the advantage of separate configs, and you can decorate at the assembly level, but has the disadvantage of if you override at the class/method level, you still have the issue.
Create your own (2) reporters. It is worth noting that if you use a reporter, it will get called, regardless as to if it is working in the environment. IEnvironmentAwareReporter allows for composite reporters, but will not prevent a direct call to the reporter.
Most likely you will need 2 reporters, one which does nothing (like a quiet reporter) but only works on your CI server, or when called by TeamCity. Will call it the TeamCity Reporter. And One, which is a multiReporter which Calls teamCity if it is working, otherwise defers to .
Use a FrontLoadedReporter (not quite ready). This is how ApprovalTests currently uses NCrunch. It does the above method in front of whatever is loaded in your UseReporter attribute. I have been meaning to add an assembly level attribute for configuring this, but haven't yet (sorry) I will try to add this very soon.
Hope this helps.
Llewellyn
I recently came into this problem myself.
Borrowing from xunit and how they deal with TeamCity logging I came up with a TeamCity Reporter based on the NCrunch Reporter.
public class TeamCityReporter : IEnvironmentAwareReporter, IApprovalFailureReporter
{
public static readonly TeamCityReporter INSTANCE = new TeamCityReporter();
public void Report(string approved, string received) { }
public bool IsWorkingInThisEnvironment(string forFile)
{
return Environment.GetEnvironmentVariable("TEAMCITY_PROJECT_NAME") != null;
}
}
And so I could combine it with the NCrunch reporter:
public class TeamCityOrNCrunchReporter : FirstWorkingReporter
{
public static readonly TeamCityOrNCrunchReporter INSTANCE =
new TeamCityOrNCrunchReporter();
public TeamCityOrNCrunchReporter()
: base(NCrunchReporter.INSTANCE,
TeamCityReporter.INSTANCE) { }
}
[assembly: FrontLoadedReporter(typeof(TeamCityOrNCrunchReporter))]
I just came up with one small idea.
You can implement your own reporter, let's call it DebugReporter
public class DebugReporter<T> : IEnvironmentAwareReporter where T : IApprovalFailureReporter, new()
{
private readonly T _reporter;
public static readonly DebugReporter<T> INSTANCE = new DebugReporter<T>();
public DebugReporter()
{
_reporter = new T();
}
public void Report(string approved, string received)
{
if (IsWorkingInThisEnvironment())
{
_reporter.Report(approved, received);
}
}
public bool IsWorkingInThisEnvironment()
{
#if DEBUG
return true;
#else
return false;
#endif
}
}
Example of usage,
[UseReporter(typeof(DebugReporter<FileLauncherReporter>))]
public class SomeTests
{
[Test]
public void test()
{
Approvals.Verify("Hello");
}
}
If test is faling, it still would be red - but reporter would not came up.
The IEnvironmentAwareReporter is specially defined for that, but unfortunatelly whatever I return there, it still calls Report() method. So, I put the IsWorkingInThisEnvironment() call inside, which is a little hackish, but works :)
Hope that Llywelyn can explain why it acts like that. (bug?)
I'm using CC.NET and I do have TortoiseSVN installed on the server.
I reconfigured my build server to allow the CC.NET service to interact with the desktop. When I did that, TortiseMerge launched. So I think what's happening is that Approvals tries to launch the tool, but it cant because CC.NET is running as a service and the operating system prevents that behavior by default. If TeamCity runs as a service, you should be fine, but you might want to test.

DLL containing custom attribute remains locked

I am trying to write an attribute to apply security to a method. Something that would look like this:
[CustomAuthorization(SecurityAction.Demand)]
public void DoSomething()
{
//Do Something
}
so I have my Attribute on another assembly:
public sealed class AuthorizationAttribute : CodeAccessSecurityAttribute
{
public override IPermission CreatePermission()
{
if (!/*authorize here*/)
{
return new CustomPermission(PermissionState.Unrestricted);
}
throw new Exception("IdentificationFailure.");
}
}
public AuthorizationAttribute(SecurityAction securityAction)
: base(securityAction) { }
}
So far it works.
I run my main program and it does its job.
Now I go and to modify the assembly having the attribute, build it. no problem.
I go back in my main program try to build and there it fails. It cannot copy the new built dll because the old one is still in use by a process.
Does anybody have any idea what would be happening here?
If you're using VS2010, there is an issue with vhost.exe not releasing the instance. You can end process on it for now until MS comes out with a fix.
It sounds like you haven't exited your main program before trying to rebuild it. Check your running processes for references to your main program or your security attribute dll. Process Explorer can be a real big help here.
Just been trouble shooting the same issue and it boiled down to the fact that we were using testaccessors to test private methods. When unloading the unittest projects, the assembly is released. Our assembly gets locked when compiling. Haven't found a solution to this yet, but have submitted a bug to ms. Are you using testaccessors?
Also see Assembly is being used by another process and https://stackoverflow.com/questions/6895038/testaccessor-impl-of-codeaccesssecurityattribute-locks-assembly
MS bug:
https://connect.microsoft.com/VisualStudio/feedback/details/682485/use-of-testaccessor-and-impl-of-codeaccesssecurityattribute-locks-assembly#details

Constructor of attribute decorating the main method does not get called in release builds

Does anyone know why the constructor of an attribute decorating the main method is called in debug builds, but not in release builds?
How can I ensure that the constructor is called in release builds as well? Without calling it manually of course.
Any insight on this subject would be very much appreciated.
I can reproduce this (in both debug and release), when executed via the IDE with the "Debug" => "Enable the Visual Studio hosting process" option enabled, via the below. At the command-line it will print "hello", where-as via the IDE it will print "world". It looks like the IDE is doing some different reflection on the attributes.
This is not expected behaviour, and you should not rely on this behaviour. If you want some particular code to execute: invoke the desired code explicitly. To get predictable behaviour, disable the Debug" => "Enable the Visual Studio hosting process" option.
using System;
public class MyTestAttribute : Attribute {
public MyTestAttribute() {
Program.text = "world";
}
}
class Program {
public static string text = "hello";
[MyTest]
static void Main() {
Console.WriteLine(text);
Console.ReadKey();
}
}

Make MSTest respect [Conditional()] attribute?

I'm using VS2010, I have the following method call:
[Conditional("DEBUG")]
public void VerboseLogging() { }
public void DoSomething() {
VerboseLogging();
Foo();
Bar();
}
Then I have a unit test for the DoSomething method which checks that it emits proper logging.
[Conditional("DEBUG"), TestMethod()]
public void EnsureVerboseLog() {
DoSomething();
VerifyVerboseLoggingCalled(); // <-- fail in release builds since VerboseLogging() calls get eliminated.
}
It seems that MSTest only sees TestMethod and executes it (generating failed test) even though I've marked it with Conditional("DEBUG") and compile it in release mode.
So, is there a way to exclude certain tests depending on compilation constant other than #if?
The ConditionalAttribute does not affect whether or not a method is compiled into an application. It controls whether or not calls to the method are compiled into the application.
There is no call to EnsureVerboseLog in this sample. MSTest just sees a method in the assemlby with the TestMethod attribute and correctly executes it. In order to prevent MSTest from running the method you will need to do one of the following
Not compile it into your application (possible through #if's)
Not annotate it with the TestMethod attribute
So, is there a way to exclude certain tests depending on compilation
constant other than #if?
I'd use the #if - it's readable.
[TestMethod]
#if !DEBUG
[Ignore]
#endif
public void AnyTest()
{
// Will invoke for developer and not in test-server!
}
HTH..
If you want to be able to conditionally run different tests, you will probably want to use the [TestCategory] attribute:
[TestMethod]
[TestCategory("SomeCategory")]
public void SomethingWorks()
{
...
}
And then use the filter parameter to include or exclude the categories:
dotnet test --filter "TestCategory=SomeCategory"
dotnet test --filter "TestCategory!=SomeCategory"
A work around is to set the Priority attribute to -1 to your method. Then run mstest with "minpriority:0" as an argument.
[TestMethod()]
[Priority(-1)]
public void Compute_Foo()
{
This method will not be executed
}

Categories