Execute C# code with side effects on breakpoint - visual-studio

I would like to use a breakpoint to execute code in Visual Studio 2017 and have the side effects of that code affect the program. It appears from Execute code when breakpoint is hit? that this was possible in a past version of Visual Studio.
But consider the code below.
int Counter = 0;
public bool Increment()
{
Counter++;
return false;
}
public void PrintNumbers()
{
for (int i = 0; i < 3; i++)
{
Debug.WriteLine($"Iteration {i}");
}
Debug.WriteLine("Counter: " + Counter);
}
I have set a breakpoint on the line that runs the "Iteration" as follows:
When this runs, it produces the following output:
Iteration 0
Iteration 1
Iteration 2
Counter: 0
This is a true technological marvel! Visual Studio does run the code -- if I change the "return false" in Increment() to "return true," the breakpoint will be hit. But it magically avoids side effects. That's great! But I want side effects. In other words, I really want an action option besides "log a message to output windows" that allows execution of code with side effects. Is there any way around this Visual Studio feature?
You might be wondering why I want to do this. I would like to make a conditional breakpoint dependent on another conditional breakpoint having been passed. (For example, I know that the Knight is talking funny after meeting the Princess, so I want to put a breakpoint on KnightTalks() that stops only if some code in KnightMeetsPrincess() has executed. I run into situations this all the time and get annoyed when I have to disable the KnightTalks breakpoint and reenable it after KnightMeetsPrincess, especially when there is a longer chain of events.) Based on Conditional breakpoint depends on other breakpoint, I think there is no built-in way to do this. But I could do something like it if I could execute arbitrary code -- for example, if I could set and get a pseudovariable. Or one could use code like the following:
public static class Bookmark
{
static Dictionary<string, int> HitCounts = new Dictionary<string, int>();
public static bool Set(string s)
{
if (!HitCounts.ContainsKey(s))
HitCounts[s] = 1;
else
HitCounts[s]++;
return false;
}
public static bool Set(string s, bool condition)
{
if (condition)
Set(s);
return false;
}
public static bool Get(string s)
{
return HitCounts.ContainsKey(s);
}
}
The idea of this code was that I would put a conditional breakpoint on KnightMeetsPrincess that called Bookmark.Set("KnightMeetsPrincess") and then on KnightTalks, a separate conditional breakpoint called Bookmark.Get("KnightMeetsPrincess"). Alas, this doesn't work because the code executes, but without side effects.
Any workarounds?

Related

Add breakpoints and install handlers

My high-level goal is something like this:
void print_backtrace() {
void *callstack[128];
int framesC = backtrace(callstack, sizeof(callstack));
printf("backtrace() returned %d addresses\n", framesC);
char** strs = backtrace_symbols(callstack, framesC);
for(int i = 0; i < framesC; ++i) {
if(strs[i])
printf("%s\n", strs[i]);
else
break;
}
free(strs);
}
install_breakpoint_handler("__NSAutoreleaseNoPool", print_backtrace);
So, each time the __NSAutoreleaseNoPool function breakpoint is catched, print_backtrace should be called. (All within the same binary. I'm not trying to catch the breakpoint of separate processes.)
I guess I can somehow do this via ptrace. Is there some easy-to-use and lightweight library?
Currently I'm searching for a MacOSX solution, but cross-platform would be nice of course.
I just found one lib (I even had used it a few years ago...): mach_override
I also found this debuglib but didn't tried.
See here for a demonstration for __NSAutoreleaseNoPool: It automatically executes print_backtrace.

Why does an async function without an await result in a compiler warning?

Can anyone explain why async functions in c# 5 are required to have at least 1 await? I can't find a clear reason/explaination.
By required, I mean that the compiler warns when an async function doesn't have any await calls inside of it, but doesn't throw a compile error.
From this answer:
Similarly, a method marked as async must have at least one await. On an await, the runtime will save the current thread's state and call stack, make the asynchronous call, and unwind back to the runtime's message loop to handle the next message and keep the app responsive. When the asynchronous operation is complete, at the next scheduling opportunity, the call stack to up the async operation is pushed back in and continued as if the call was synchronous.
But from msdn:
If the method does not contain an await expression or statement, then it executes synchronously. A compiler warning alerts you to any async methods that don't contain await because that situation might indicate an error.
What type of error occur that merits this being a compiler warning versus just recommended usage?
MSDN has a good description for this warning: Compiler Warning (level 1) CS4014. The good quote from it will be:
In most cases, that behavior isn't what you expect.
I think that the main reason why this warning exists is that async/await is not really obvious, and developers usually do mistakes like I will describe below.
For example you have a method, which do something heavy for several seconds:
public int DoSomething()
{
int sum = 0;
for (int i = 0; i < 10; i++)
{
sum += i;
Thread.Sleep(1000);
}
return sum;
}
You heard about async and await somewhere and you want to try them. And this is how very often people think that they will move everything to background (maybe not often, but I thought that this is how it works before I read more documentation, so we can count at least me):
public async Task<int> DoSomething()
{
int sum = 0;
for (int i = 0; i < 10; i++)
{
sum += i;
Thread.Sleep(1000);
}
return sum;
}
You think that problem is solved, but warning tells you that without await you should not use async, because it just does nothing, all your code will run synchronously, the last sample with async is similar to next code:
public Task<int> DoSomething()
{
int sum = 0;
for (int i = 0; i < 10; i++)
{
sum += i;
Thread.Sleep(1000);
}
return Task.Result<int>(sum);
}
Where you do everything on the same context, which calls this method.
So the main reason of this warning I think to let people know that probably they use async wrong and this is a time to read documentation.

Calling functions while in debug mode in VC++ (Immediate Window)

I wonder can I call functions during the debug mode in VC++? Assume that I have a function to which I set a break point at, when the execution stops at that point during debugging, can I call other functions and see their results before proceeding to the next line of code?
I believe you can. I think its called Immediate Window. I use VS2010 Ultimate, so I don't know if it exists in your version.
Ctrl + Alt + I
But this only prints output for when the function returns a value. Also, it may not work in some cases.
Let's say you have :
#include <iostream>
int number = 10; //global
void setNumber(int n);
int main()
{
std::cout<<std::endl; //breakpoint 1 here
setNumber(4);
std::cout<<std::endl; //breakpoint 2 here
}
int getNumberSquared()
{
return number * number;
}
void setNumber(int n)
{
number = n;
}
when you encounter breakpoint 1, press the shortcut and type:
getNumberSquared()
The output will be 100
After encountering breakpoint 2, do the same thing and the output will be 16
Visual studio has the option to jump to a specific statement (right click + set next statement or ctrl+shift+F10), but be aware when doing so. A function call requires registries to be valid, which will most likely not be if you jump across classes or out of scope.

In VS, make print-on-breakpoint use the console

I've made a "when hit, print a message" breakpoint in VS 2010. It works, but it only outputs to the VS "output" pane. Can I make it use my app's console window?
I've tried:
Debug.Listeners.Add(new ConsoleTraceListener());
As well as:
var writer = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(writer);
It is possible to print this message in your app console window, but for that you need to use the debugger evaluator:
Create a method that you would like to call from the debugger when the breakpoint is hit.
Place a breakpoint, but instead of providing just a text message use your method name in curly braces, eg. {CallFromDebugger()}
Have a look at this code:
static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
[Conditional("DEBUG")]
static void MessageFromDebugger(string message)
{
Console.WriteLine("I was called from the debugger evaluator: {0}", message);
}
If you place a breakpoint on line 5: Console.WriteLine(i); with When Hit... property set to: {MessageFromDebugger("message from address $ADDRESS")} you should see in your console window:
0
I was called from the debugger evaluator: message from address ConsoleApplication1.Program.Main(string[]) + 0x00000048
1
I was called from the debugger evaluator: message from address ConsoleApplication1.Program.Main(string[]) + 0x00000048
2
What's interesting is that you may pass arguments to your function that are valid in the calling scope as well as the special debugger variables (such as $ADDRESS, $PID, $CALLSTACK etc.). I observed though that special debugger variables are just placeholders and are replaced before submitting to your function, so remember to put them in double quotes, eg. {MessageFromDebugger(#"$CALLSTACK")}

Hotkeys for Previous and Next call stack frames in Visual Studio

Visual Studio gives many navigation hotkeys:
F8 for next item in current panel (search results, errors ...),
Control+K, N for bookmarks,
Alt+- for going back and more.
There is one hotkey that I can't find, and I can't even find the menu-command for it, so I can't create the hotkey myself.
I don't know if such exist: Previous and Next call-stack frame.
I try not using the mouse when programming, but when I need to go back the stack, I must use it to double click the previous frame.
Anyone? How about a macro that does it?
I wrote 2 macros to gain it: PreviousStackFrame and NextStackFrame and assigned shortcuts to
Function StackFrameIndex(ByRef aFrames As EnvDTE.StackFrames, ByRef aFrame As EnvDTE.StackFrame) As Long
For StackFrameIndex = 1 To aFrames.Count
If aFrames.Item(StackFrameIndex) Is aFrame Then Exit Function
Next
StackFrameIndex = -1
End Function
Sub NavigateStack(ByVal aShift As Long)
If DTE.Debugger.CurrentProgram Is Nothing Then
DTE.StatusBar.Text = "No program is currently being debugged."
Exit Sub
End If
Dim ind As Long = StackFrameIndex(DTE.Debugger.CurrentThread.StackFrames, DTE.Debugger.CurrentStackFrame)
If ind = -1 Then
DTE.StatusBar.Text = "Stack navigation failed"
Exit Sub
End If
ind = ind + aShift
If ind <= 0 Or ind > DTE.Debugger.CurrentThread.StackFrames.Count Then
DTE.StatusBar.Text = "Stack frame index is out of range"
Exit Sub
End If
DTE.Debugger.CurrentStackFrame = DTE.Debugger.CurrentThread.StackFrames.Item(ind)
DTE.StatusBar.Text = "Stack frame index: " & ind & " of " & DTE.Debugger.CurrentThread.StackFrames.Count
End Sub
Sub PreviousStackFrame()
NavigateStack(1)
End Sub
Sub NextStackFrame()
NavigateStack(-1)
End Sub
I have solved this problem with AutoHotkey. I made this a few months ago.
Suppose you wanted to use Control+1 and Control+2 and that Control+Alt+C is bound to showing the Call Stack window:
^1::SendInput !^c{down}{enter}
^2::SendInput !^c{up}{enter}
It seems to work pretty well. If you aren't already using AutoHotkey to show Visual Studio who's boss, please give it a shot. Your question indicates that you would benefit greatly from it. It's a game changer. Good luck.
I don't think theres an explict next-frame / prev-frame key binding but heres what I do.
CTRL-ALT-C is already bound to "Debug.CallStack"
This will focus you in the Call Stack Tool Window
Once focused in the Callstack window... Up & Down arrows will move you through the call stack frames
I've then bound
CTRL-C, CTRL-S to "DebuggerContextMenus.CallStackWindow.SwitchToFrame"
and
CTRL-C, CTRL-C to "DebuggerContextMenus.CallStackWindow.SwitchToCode"
both of which will take you back into the code window at the particular frame.
Hope that helps.
Huge thanks to #Oleg Svechkarenko for his answer that gave me a starting point in crafting this solution. Since modern versions of Visual Studio no longer have official macro support, this should drop in for those who use the Visual Commander plugin, but probably can be easily adapted for any other macro extension.
Here is the code for the command, I created one for navigating up the call stack and one for navigating down with the same code except the parameter passed to MoveStackIndex(), then bound keyboard shortcuts to them:
using EnvDTE;
using EnvDTE80;
using System;
public class C : VisualCommanderExt.ICommand
{
enum MoveDirection
{
Up,
Down,
}
private bool IsValidFrame(StackFrame frame)
{
string language = frame.Language;
bool result = (language == "C#" ||
language == "C++" ||
language == "VB" ||
language == "Python");
return result;
}
private void MoveStackIndex(EnvDTE80.DTE2 DTE, MoveDirection direction)
{
StackFrame currentFrame = DTE.Debugger.CurrentStackFrame;
bool foundTarget = false;
bool pastCurrent = false;
StackFrame lastValid = null;
foreach (StackFrame frame in DTE.Debugger.CurrentThread.StackFrames)
{
bool isCurrent = frame == currentFrame;
if (direction == MoveDirection.Down)
{
if (isCurrent)
{
if (lastValid == null)
{
// No valid frames below this one
break;
}
else
{
DTE.Debugger.CurrentStackFrame = lastValid;
foundTarget = true;
break;
}
}
else
{
if (IsValidFrame(frame))
{
lastValid = frame;
}
}
}
if (direction == MoveDirection.Up && pastCurrent)
{
if (IsValidFrame(frame))
{
DTE.Debugger.CurrentStackFrame = frame;
foundTarget = true;
break;
}
}
if (isCurrent)
{
pastCurrent = true;
}
}
if (!foundTarget)
{
DTE.StatusBar.Text = "Failed to find valid stack frame in that direction.";
}
}
public void Run(EnvDTE80.DTE2 DTE, Microsoft.VisualStudio.Shell.Package package)
{
if (DTE.Debugger.CurrentProgram == null)
{
DTE.StatusBar.Text = "Debug session not active.";
}
else
{
// NOTE: Change param 2 to MoveDirection.Up for the up command
MoveStackIndex(DTE, MoveDirection.Down);
}
}
}
Look in Tools->Options->Environment->Keyboard. Enter "stack" or "frame" and related menus will appear. It seems that there's no next and previous call-stack frame.

Resources