How can I watch multiple variables while debugging without stopping at breakpoints?

Viewed 674

Suppose I have a complex C++ application that I need to debug with a lot of variables. I wanna avoid using std::cout and printf approaches (below there's an explaination why).

In order to explain my issue, I wrote a minimal example using chrono (This program calculates fps of its while cycle over time and increment i_times counter until it reaches 10k):

#include <chrono>

using chrono_hclock = std::chrono::high_resolution_clock;

int main(int argc, char** argv){
    bool is_running = true;
    float fps;
    int i_times=0;
    chrono_hclock::time_point start;
    chrono_hclock::time_point end;

    while(is_running){
         start = chrono_hclock::now();

         // Some code execution

         end = chrono_hclock::now();

         fps=(float)1e9/(float)std::chrono::duration_cast<std::chrono::nanoseconds>(end-start).count());
         if(++i_times==10000) is_running=false;
    }

    return 0;
}

I would like to debug this program and watch for fps and i_times variables continuosly over time, without stopping execution.

Of course I can simply use std::cout, printf or other means to output variables values redirecting them to stdout or a file while debugging and those are OK for simple types, but I have multiple variables which data type are struct-based and it would be creepy, time expensive and code bloating to write instructions to print each one of them. Also my application is a realtime video/audio H.264 encoder streaming with RTSP protocol and stopping at breakpoints means visualizing artifacts in my other decoder application because the encoder can't keep up with the decoder (because the encoder hit a breakpoint).

How can I solve this issue?

Thanks and regards!


The IDE I'm currently using for developing is Visual Studio 2019 Community.

I'm using the Local Windows Debugger.

I'm open to using alternative open source IDEs like VSCode or alternative debugging methods to solve this problem and/or to not be confinated into using a specific IDE.

To watch for specific multiple variables in VS I use the built-in Watch Window. While debugging with LWD, I add manually variables by right-clicking them in my source code and click Add Watch. Then those are showed in the Watch Window (Debug-Windows-Watch-Watch 1): watch_window_1

However I can only watch this window contents once I hit a breakpoint I set inside the while cycle, thus blocking execution, so that doesn't solve my issue.

2 Answers

You can use nonblocking breakpoint. First add the breakpoint. Then click on breakpoint settings or right click and select action.
enter image description here
Now you add a message like any string that is suggestive for you. And in brackets include the values to show, for instance

value of y is {y} and value of x is {x}

In the image is shown the value of i when it hits the breakpoint. Check the "Continue code execution" so breakpoint will not block execution. The shape of your breakpoint will change to red diagonal square. You can add also specific conditions if you click the Conditions checkbox.
Now while debugging all these debug messages will be shown in the output window:
enter image description here
In the above image it is showing the following message:

the value of i is {i}

By checking the "Conditions" you can add specific conditions, for instance i%100==0 and it will show the message only if i is divisible by 100.
enter image description here
This time your breakpoint will be marked with a + sign, meaning it has condition. Now while debugging there will be shown the i only when divisible by 100, so you can restrict the output to some more meaningful cases
enter image description here

The strict answer is "no" but...

I think I understand what you're trying to accomplish. This could be done by dumping the watched variables into to shared memory which is read by 2nd process. A watch and a break point in the 2nd would allow you to see the values in Visual Studio without interrupting the original application.

A few caveats:

  • UAC must be admin on both sides to open the memory handle
  • This wouldn't work with pointers as the 2nd program only has access to the shared memory
  • Windows anti-virus went nuts for the first few times I ran this but eventually calmed down

Worker application:

#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <windows.h>
#include <chrono>
#include <thread>

PCWSTR SHARED_MEMORY_NAME = L"Global\\WatchMemory";

struct watch_collection  // Container for everything we want to watch
{
    int i;
    int j;
    int k;
};

using chrono_hclock = std::chrono::high_resolution_clock;


int main(int argc, char** argv)
{
    bool is_running = true;
    float fps;
    int i_times = 0;
    chrono_hclock::time_point start;
    chrono_hclock::time_point end;
    HANDLE map_file;
    void* shared_buffer;

    // Set up the shared memory space
    map_file = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(watch_collection), SHARED_MEMORY_NAME);
    if (map_file == NULL)
    {
        return 1;  // Didn't work, bail.  Check UAC level!
    }

    shared_buffer = MapViewOfFile(map_file, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(watch_collection));
    if (shared_buffer == NULL)
    {
        CloseHandle(map_file);  // Didn't work, clean up the file handle and bail.
        return 1;  
    }

    // Do some stuff
    while (is_running) {
        start = chrono_hclock::now();

        for (int i = 0; i < 10000; i++)
        {
            for (int j = 0; j < 10000; j++)
            {
                for (int k = 0; k < 10000; k++) {
                    watch_collection watches { i = i, j = j, k = k };
                    CopyMemory(shared_buffer, (void*)&watches, (sizeof(watch_collection))); // Copy the watches to the shared memory space

                    // Do more things...
                }
            }
        }

        end = chrono_hclock::now();

        fps = (float)1e9 / (float)std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
        if (++i_times == 1000000) is_running = false;
    }

    // Clean up the shared memory buffer and handle
    UnmapViewOfFile(shared_buffer);
    CloseHandle(map_file);

    return 0;
}

Watcher application:

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#pragma comment(lib, "user32.lib")

PCWSTR SHARED_MEMORY_NAME = L"Global\\WatchMemory";

struct watch_collection  // Container for everything we want to watch
{
    int i;
    int j;
    int k;
};


int main()
{
    HANDLE map_file;
    void* shared_buffer;
    bool is_running = true;

    watch_collection watches; // Put a watch on watches

    // Connect to the shared memory
    map_file = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHARED_MEMORY_NAME);
    if (map_file == NULL)
    {
        return 1; // Couldn't open the handle, bail.  Check UAC level!
    }

    shared_buffer = MapViewOfFile(map_file, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(watch_collection));
    if (shared_buffer == NULL)
    {
        CloseHandle(map_file);
        return 1;
    }

    // Loop forever
    while (is_running)
    { 
        CopyMemory((void*)&watches, shared_buffer, (sizeof(watch_collection)));  
    } // Breakpoint here

    UnmapViewOfFile(shared_buffer);
    CloseHandle(map_file);

    return 0;
}
Related