如何在调试时观察多个变量而不在断点处停止?
How can I watch multiple variables while debugging without stopping at breakpoints?
假设我有一个复杂的 C++ 应用程序,需要使用大量变量进行调试。我想避免使用 std::cout
和 printf
方法(下面解释原因)。
为了解释我的问题,我写了一个 minimal 例子,使用 chrono
(这个程序计算它的 while fps
随着时间的推移循环并递增 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;
}
我想调试这个程序并随时间连续观察 fps
和 i_times
变量,不停止执行。
当然我可以简单地使用 std::cout
、printf
或其他方式输出变量值,在调试时将它们重定向到 stdout
或文件,这些对于简单类型是可以的,但是我有多个变量,这些变量的数据类型是基于结构的,并且编写指令来打印每个变量会令人毛骨悚然,耗时且代码膨胀。此外,我的应用程序是实时 video/audio H.264 编码器流式传输,使用 RTSP 协议并在断点处停止意味着在我的其他解码器应用程序中可视化工件,因为编码器无法跟上解码器(因为编码器遇到断点) .
如何解决这个问题?
谢谢和问候!
我目前用于开发的 IDE 是 Visual Studio 2019 Community.
我正在使用 本地 Windows 调试器。
我愿意使用 IDE 之类的替代开源 VSCode 或替代调试方法来解决此问题 and/or 而不是局限于使用特定的 IDE.
要在 VS 中观察特定的多个变量,我使用内置的 Watch
Window。在使用 LWD
进行调试时,我通过在源代码中右键单击变量并单击 Add Watch
来手动添加变量。然后在Watch
Window (Debug-Windows-Watch-Watch 1)中显示:
然而,一旦我遇到我在 while cycle
中设置的断点,我只能 观看此 window 内容,因此 阻止执行,所以这不能解决我的问题。
您可以使用非阻塞断点。首先添加断点。然后单击断点设置或右键单击并select 操作。
现在您添加一条消息,就像任何对您有暗示意义的字符串一样。括号中包含要显示的值,例如
value of y is {y} and value of x is {x}
图中显示了遇到断点时 i 的值。检查 "Continue code execution" 这样断点就不会阻止执行。断点的形状将变为红色对角正方形。如果单击“条件”复选框,您还可以添加特定条件。
现在调试所有这些调试消息将显示在输出 window:
在上图中,它显示了以下消息:
the value of i is {i}
通过勾选 "Conditions" 你可以添加特定条件,例如 i%100==0 并且只有当 i 被 100 整除时它才会显示消息。
这次您的断点将标有 + 号,表示它有条件。现在在调试时,只有在被 100 整除时才会显示 i,因此您可以将输出限制在一些更有意义的情况下
严格的答案是 "no" 但是...
我想我理解你想要完成的事情。这可以通过将监视的变量转储到由第二个进程读取的共享内存来完成。手表和第二个断点可以让您在不中断原始应用程序的情况下查看 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;
}
假设我有一个复杂的 C++ 应用程序,需要使用大量变量进行调试。我想避免使用 std::cout
和 printf
方法(下面解释原因)。
为了解释我的问题,我写了一个 minimal 例子,使用 chrono
(这个程序计算它的 while fps
随着时间的推移循环并递增 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;
}
我想调试这个程序并随时间连续观察 fps
和 i_times
变量,不停止执行。
当然我可以简单地使用 std::cout
、printf
或其他方式输出变量值,在调试时将它们重定向到 stdout
或文件,这些对于简单类型是可以的,但是我有多个变量,这些变量的数据类型是基于结构的,并且编写指令来打印每个变量会令人毛骨悚然,耗时且代码膨胀。此外,我的应用程序是实时 video/audio H.264 编码器流式传输,使用 RTSP 协议并在断点处停止意味着在我的其他解码器应用程序中可视化工件,因为编码器无法跟上解码器(因为编码器遇到断点) .
如何解决这个问题?
谢谢和问候!
我目前用于开发的 IDE 是 Visual Studio 2019 Community.
我正在使用 本地 Windows 调试器。
我愿意使用 IDE 之类的替代开源 VSCode 或替代调试方法来解决此问题 and/or 而不是局限于使用特定的 IDE.
要在 VS 中观察特定的多个变量,我使用内置的 Watch
Window。在使用 LWD
进行调试时,我通过在源代码中右键单击变量并单击 Add Watch
来手动添加变量。然后在Watch
Window (Debug-Windows-Watch-Watch 1)中显示:
然而,一旦我遇到我在 while cycle
中设置的断点,我只能 观看此 window 内容,因此 阻止执行,所以这不能解决我的问题。
您可以使用非阻塞断点。首先添加断点。然后单击断点设置或右键单击并select 操作。
现在您添加一条消息,就像任何对您有暗示意义的字符串一样。括号中包含要显示的值,例如
value of y is {y} and value of x is {x}
图中显示了遇到断点时 i 的值。检查 "Continue code execution" 这样断点就不会阻止执行。断点的形状将变为红色对角正方形。如果单击“条件”复选框,您还可以添加特定条件。
现在调试所有这些调试消息将显示在输出 window:
在上图中,它显示了以下消息:
the value of i is {i}
通过勾选 "Conditions" 你可以添加特定条件,例如 i%100==0 并且只有当 i 被 100 整除时它才会显示消息。
这次您的断点将标有 + 号,表示它有条件。现在在调试时,只有在被 100 整除时才会显示 i,因此您可以将输出限制在一些更有意义的情况下
严格的答案是 "no" 但是...
我想我理解你想要完成的事情。这可以通过将监视的变量转储到由第二个进程读取的共享内存来完成。手表和第二个断点可以让您在不中断原始应用程序的情况下查看 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;
}