【问题标题】:How can I watch multiple variables while debugging without stopping at breakpoints?如何在调试时观察多个变量而不在断点处停止?
【发布时间】:2020-06-09 11:51:40
【问题描述】:

假设我有一个复杂的 C++ 应用程序,我需要使用很多变量进行调试。我想避免使用std::coutprintf 方法(下面有解释原因)。

为了解释我的问题,我使用chrono 编写了一个minimal 示例(该程序计算fps 的while 循环随着时间的推移并递增i_times 计数器直到它达到 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;
}

我想调试这个程序并随着时间的推移持续观察fpsi_times 变量,不停止执行

当然,我可以简单地使用std::coutprintf 或其他方式输出变量值,在调试时将它们重定向到stdout 或文件,这些对于简单类型来说是可以的,但是我有多个数据类型的变量是基于结构的,编写指令来打印它们中的每一个都是令人毛骨悚然、耗时且代码臃肿的。此外,我的应用程序是一个实时视频/音频 H.264 编码器,使用 RTSP 协议进行流式传输,在断点处停止意味着在我的其他解码器应用程序中可视化伪影,因为编码器无法跟上解码器的速度(因为编码器遇到断点)。

我该如何解决这个问题?

感谢和问候!


我目前用于开发的 IDE 是 Visual Studio 2019 Community

我正在使用本地 Windows 调试器

我愿意使用 VSCode 等替代开源 IDE 或替代调试方法来解决此问题和/或不局限于使用特定 IDE。

要在 VS 中查看特定的多个变量,我使用内置的 Watch 窗口。在使用LWD 进行调试时,我通过在源代码中右键单击变量并单击Add Watch 来手动添加变量。然后这些显示在Watch Window (Debug-Windows-Watch-Watch 1)中:

但是,当我遇到我在while cycle 中设置的断点时,我可以查看此窗口内容,从而阻止执行,所以这并不能解决我的问题.

【问题讨论】:

  • 在 while(true) 循环执行后简单地将其写入文件怎么样?制作一些容器,在其中添加或设置值 container[i_times] = fps;

标签: c++ debugging ide


【解决方案1】:

您可以使用非阻塞断点。首先添加断点。然后点击断点设置或者右键选择动作。

现在,您添加一条消息,就像对您有提示性的任何字符串一样。括号中包括要显示的值,例如

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

在图像中显示了 i 在遇到断点时的值。选中“继续代码执行”,这样断点就不会阻塞执行。断点的形状将变为红色对角正方形。如果单击“条件”复选框,您还可以添加特定条件。
现在,在调试时,所有这些调试消息都将显示在输出窗口中:

在上图中,它显示以下消息:

the value of i is {i}

通过检查“条件”,您可以添加特定条件,例如 i%100==0,只有当 i 可以被 100 整除时才会显示消息。

这次你的断点将被标记为 + 号,表示它有条件。现在在调试时,只有在被 100 整除时才会显示 i,因此您可以将输出限制为一些更有意义的情况

【讨论】:

  • 我用了 VS 十多年了,从来没有注意到!我认为这应该是公认的答案!
  • 我今天试过这个方法。它又快又好!我还尝试在我的项目中访问具有两个变量(例如 AVRational)的结构,该结构具有 num=1 和 den=44100 并且我在输出控制台中得到了 {num=1 den=44100 },因此它也可以打印成员值而不必明确。赏金奖励给你,但只是一些注意事项:在使用这些断点进行调试时,由于内存分析,程序减慢了 5-10 倍,我也想知道是否可以扩展输出而不是读取...对于更复杂的结构。
  • 但是我可以只输入我需要的变量,所以这解决了我的任务。谢谢。
  • @FabioCrispino 速度变慢了,不仅是因为内存分析,还因为调试输出。但是通过添加一些断点条件可以最小化输出。输出可以扩展到更复杂的结构和类。
【解决方案2】:

严格的答案是“不”,但是......

我想我理解您想要完成的工作。这可以通过将监视的变量转储到由第二个进程读取的共享内存来完成。 2nd 中的监视和断点将允许您在 Visual Studio 中查看值,而不会中断原始应用程序。

一些注意事项:

  • UAC 必须两边都是 admin 才能打开内存句柄
  • 这不适用于指针,因为第二个程序只能访问共享内存
  • Windows 杀毒软件最初几次都发疯了 跑了这个,但最终平静下来

工作者应用程序:

#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;
}

观察者应用:

#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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-18
    • 2011-03-13
    • 2019-08-16
    • 1970-01-01
    • 2012-02-16
    • 2023-03-12
    • 2018-08-11
    • 1970-01-01
    相关资源
    最近更新 更多