Visual Studio 高级调试-代码调试

news2024/9/23 9:30:50

概述

编程圈子里隔三差五的就会有场“谁是最强IDE”之争,重要的是我们需要对使用的IDE有充分的了解,正所谓工欲善其事,必先利其器。

本文主要讲述Visual Studio常用的调试技巧,包括多类型断点,数据监视,以及多线程调试等。下篇则侧重于性能分析和Visual Studio Enterprise 版本所特有的一些高级功能。

调试原理

PDB文件

PDB被称为符号文件或程序数据库文件,它主要用来保存源代码和二进制指令文件(dll)之间的映射关系。主要用途有两个:1.指出二进制指令所对应的代码位置,即源文件和行号;2.帮调试器确定断点位置。

Visual Studio在Release模式下也会生成.pdb文件,但这并不意味着我们项目发布需要它。事实上,由于.pdb是编译时与.dll一起生成的,因此它只会记录当时代码与dll的映射关系,随着存储库代码的变更,它的价值会越来越低。并且它是保存有编译机器上的代码路径信息的,因此存在安全隐患。

有同学会将堆栈信息与pdb的关系搞混,异常发生时的堆栈信息是CLR生成的,跟pdb没有任何关系。

Visual Studio要求pdb版本与所调试代码版本必须完全一致。

调试外部代码

Visual Studio默认开启仅我的代码调试,如果希望能直接调试第三方代码,需要开启外部代码调试:

  1. 调试 ->常规 -> 启用.NET Framework单步执行 & 启用源服务器支持

  1. 调试 -> 符号 -> 符号文件(.pdb)位置,勾选Microsoft 和Nuget符号服务器

启动调试后,Visual Studio会首先将第三方模块的pdb文件下载到本地目录,再根据pdb文件中指出的源文件地址,到源文件服务器下载源文件,这一切完成之后,我们的断点才可以被命中,之后再按F11即可调试第三方源代码。

启用外部代码调试通常比较耗时,一般不推荐使用。

调试System.Collections.Generic.List<T>源码

断点

一切调试的前提都基于断点,Visual Studio为我们提供了多种类型的断点,熟悉不同类型的断点并在合适的场景下运用它们,可以提高调试效率。

★表达式断点★

用当前可访问对象来设置一条bool表达式,当表达式条件为true时才会命中的断点

命中次数断点

为循环语句而设计的断点,假如你认为程序循环执行到一定次数将会出错时可以使用这种断点。

一次性断点

顾名思义,命中一次之后会自动消失的断点。

前置依赖断点

必须先命中指定的断点,才能被命中的断点。

函数名断点

当函数有多个重载或函数名称重复时,可以设置函数名断点,它会在调试时自动为所有函数设置断点。

函数名断点

数据监视

调试的目的是为了观察程序当前的运行状态,在Visual Studio中我们主要通过以下几种窗口来观察当前数据。

局部变量窗口

我们可以查看当前作用域内的对象或者变量值。

监视窗口

当前数据较多时,更方便的查看某个对象或者变量值;也可用来追踪超范围对象,监视函数返回值等。

★即时窗口★

可以对当前作用域内的元素进行运算,或者执行某个方法获取结果,为我们推断程序运行结果,排查复杂问题提供了极大的便利。

自动窗口

可以在函数执行完之后查看到函数的返回值,而不必在调试时创建一个变量来查看执行结果。

超范围对象跟踪

在局部变量窗口为对象创建一个id,并且添加到监视变量窗口当中。这样即使变量离开了当前代码的监测范围,我们依然可以观察到该对象的实时状态。

集合可视化

从Visual Studio 2022开始支持集合对象的可视化,集合中的元素可以用表格的形式展现在VS中,并且支持数据导出。

多线程调试

多线程调试则是开发者必须要掌握的重要技能。Visual Studio用于调试线程的主要工具有线程窗口、线程标记、并行堆栈窗口、并行监视窗口等,我们可以查看并行堆栈信息,进行线程标记,切换,冻结等操作。

模拟多线程场景

同学们应该都了解过线程安全集合与非线程安全集合相关知识,我们先用以下代码来模拟一个非线程安全的List。

class SampleList
{
    public int Index = 0;
    public int[] Array = new int[100];

    public void Add(int i)
    {
        if(Index > 99)
        {
            return;
        }

        Array[Index] = i;
        Index++;

        Console.WriteLine($"Insert {i} at array index:{Index}");
    }
}

然后我们在Main函数中创建1个SampleList,使用5个线程同时插入数据,观察不同线程中Index和Array的值。

var sample = new SampleList();
for(int i = 0; i < 5; i++)
{
    new Thread(() =>
    {
        for(int j = 0; j < 20; j++)
        {
            sample.Add(1);
            Thread.Sleep(500);
        }
    }).Start();
}

Console.ReadKey();

在源中显示线程

点击Visual Studio菜单中的"双绞线"图标,开启在源中显示线程。

它的作用是在源代码中标记不同的线程正在执行的位置,和断点显示的位置一样,它会显示一个双绞线图标。我们还可以右击该图标进行线程切换。

线程窗口

开始调试,等程序运行几秒钟后打上断点,在线程窗口我们可以观察到进程当前的所有线程状态。我们可以查看线程ID,类型,当前所在位置等,你还可以选择窗口上方的"列"来展示更多信息。

当我们命中断点之后,所有的线程都会停下来。展开线程调用堆栈,我们可以清楚的观察到当前有五个线程正在执行SampleList.Add() 方法,除了主线程外,还有一些.NET项目自身的基础线程在工作。

并行堆栈

并行堆栈展示的是当前线程之间的调用关系,以及堆栈信息。我们可以切换到视图模式,可以导出。并且因为C#中具有更高级别的抽象"线程":Task, 并行堆栈窗口还支持任务这种异步逻辑堆栈。

并行监视

前面我们已经观察到,当前有5个线程正在执行SampleList.Add()方法,而监视窗口只能观察到当前线程的数据,因此我们需要用到并行监视窗口。窗口中会显示每个线程中 Index和Array 的值。

线程标记

假如我们希望只看执行SampleList.Add() 方法方法的5个线程,则可以将它们设置为标记状态,该状态可以用于筛选线程。选择"仅显示标记线程"之后,并行监视窗口也会同步筛选条件。

跟踪单个线程

在别处再打一个断点,此时你按F5进行调试时,会发现调试器将以乱序进行工作,有没有办法在同一线程内进行调试呢?我们可以利用前面所提到的条件断点来实现这一操作。

使用筛选器,设置ThreadId,这样断点就只会在指定线程执行时命中了。

冻结线程

有时候,我们希望某个线程停下来,或者仅让一个线程单独执行,或者让线程以我们想要的顺序来执行,以此来观察程序状态,比如解决死锁问题等。

这时候可以利用线程冻结操作来完成。如图所示,我冻结了其余4个线程,仅让线程23908执行,此时使用F10、F11调试就与我们熟悉的单线程调试的情况无异了。

当前线程23908正在执行第26行,而其他线程则冻结在了34行。

冻结线程23908,解冻线程4744, 按F11, 调试将从线程4744堆栈所指示的位置(第34行)往下执行。

多任务调试

因为有更高级别的抽象库:System.Threading.Tasks.Task,所以C#中很少直接使用Thread来编写并行代码。Task并不是与线程一一对应的,一个线程可能会执行多个Task(C#并行编程: 从线程,线程池到任务_郭麻花的博客),Visual Studio同样支持基于Task类型的多线程调试。

并行堆栈窗口

将代码改造成Task模式,我们可以在并行堆栈窗口查看执行Task的线程信息:

任务窗口

监视窗口

方法视图

我们还可以通过切换并行堆栈窗口为方法视图,查看当前任务调用关系。可以看到由主线程产生五个异步任务正在执行SampleList.Add(), 而其中一个正在执行Console.WriteLine(), 我们可以清楚的看到Console.WriteLine()的调用堆栈信息。

热重载

热重载指的是在程序运行时可以直接修改源代码并且继续运行,无需重新启动调试。目前绝大部分.NET项目都支持热重载技术。

异常中断

即使我们在程序中使用了try-catch,但在调试时可能仍希望出现异常时能够及时的中断,以便排查问题。我们只需要在Visual Studio使用 调试 -> 异常设置 将指定异常设置成引发中断即可,并且异常可以用dll名称作为触发筛选条件。

总结

到这里我们介绍了Visual Studio中常用的代码调试技巧,Visual Studio还有支持远程调试,附加进程调试,多进程调试等,因为我不常用,所以就不介绍了。

实际开发当中调试技术并不是最重要的,最重要的是调试思路,思路(70%)+技巧(30%)=更高效的调试。就好比中医所讲的“望闻问切”,观察现象,思考原因,查看代码,调试是最后一步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/355663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

剑指 Offer 59 - I. 滑动窗口的最大值

摘要 剑指 Offer 59 - I. 滑动窗口的最大值 一、大顶堆求解 对于每个滑动窗口&#xff0c;我们可以使用 O(k) 的时间遍历其中的每一个元素&#xff0c;找出其中的最大值。对于长度为n的数组 nums而言&#xff0c;窗口的数量为 n−k1&#xff0c;因此该算法的时间复杂度为 O(…

在线图片转gif怎么操作?试试这一招在线制作gif

静图怎么变成gif动图&#xff1f;想要将手中的静态图片变成gif动图又不想下载软件的时候要怎么操作呢&#xff1f;很简单&#xff0c;通过使用【GIF中文网】的动图在线制作&#xff08;https://www.gif.cn/&#xff09;功能&#xff0c;两招就能在线制作gif图片&#xff0c;方便…

小红书购物笔记在哪里看?

小红书购物笔记在哪里看&#xff1f;#小红书带货#小红书变现#小红书运营#小红书营销#内容营销 在现在这个时代&#xff0c;网上购物已经成为一种日常的行为。每天大量的人在通过小红书购买他们心仪的商品&#xff0c;生活态度也越来越追求高品质。小红书不但能够让用户在网上购…

Metasploit框架基础(二)

文章目录前言一、Meatsplooit的架构二、目录结构datadocumentationlibmodulesplugins三、Measploit模块四、Metasploit的使用前言 Metasploit是用ruby语言开发的&#xff0c;所以你打开软件目录&#xff0c;会发现很多.rb结尾的文件。ruby是一门OOP的语言。 一、Meatsplooit的…

【opencv源码解析0.1】opencv库VS环境配置

opencv环境配置 感谢大家学习这门教程。本系列文章首发于公众号【周旋机器视觉】。 这个这门课程的第一篇文章&#xff0c;主要是opencv环境配置。 本教程的环境为 Visual Studio 2019CMake 3.22.3opencv 4.6.0windows 10 1、opencv的源码下载与安装 直接访问opencv官网&…

MySQL数据库优化————COUNT优化

直接进入主题 索引对count语句的影响 在我们对departments表进行count查询时&#xff0c;使用了以下语句 select count(*) from employees;当前employees表索引情况如图 只有一个主键索引 执行 explain select count(*) from employees;从结果中可以看到&#xff0c;这时…

NOIP2014-提高组初赛C语言解析(选择填空题)

第二十届(2014年)全国青少年信息学奥林匹克联赛初赛一、单项选择题&#xff08;共 20 题&#xff0c;每题 1.5 分&#xff0c;共计 30 分。每题有且仅有一个正确选项&#xff09;1. 以下哪个是面向对象的高级语言&#xff08; B &#xff09;A.汇编语言 B.C C.Fortran D.Basic参…

【opencv源码解析0.4】如何使用cmake来管理项目

如何使用cmake来管理项目 【opencv源码解析0.1】VS如何优雅的配置opencv环境 【opencv源码解析0.2】如何编译opencv库源码 【opencv源码解析0.3】调试opencv源码以及使用cmake来管理项目 前面几篇文章我们都是围绕Visual Studio 2019这个IDE来展开的&#xff0c;IDE为我们做了…

矩阵中的路径-剑指Offer-java深度优先

一、题目描述给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是那些水平相…

k8s部署mysql+初始化数据

1. 准备工作 1.k8s的前置内容需要提前了解 2.mysql的初始化数据 3.docerk相关知识点需要了解 2. 部署步骤 初始化数据文件准备&#xff0c;准备了nacos的一张表sql脚本&#xff0c;需要修改一点点内容 文件名称&#xff1a;init-nacos.sql 部分内容显示&#xff1a; 主要创建…

潘长江张杰再现狂飙名场面,一般人把握不住

潘长江张杰再现狂飙名场面&#xff0c;一般人把握不住#我们的客栈#高启强#老墨我饿了 在昨晚播出的《我们的客栈》在猜人游戏的环节中&#xff0c;张杰和潘长江商量了一番&#xff0c;决定还原《狂飙》某一名场面&#xff0c;没想到被张维伊一秒猜出&#xff0c;张维伊还说这是…

图像分类竞赛进阶技能:OpenAI-CLIP使用范例

OpenAI-CLIP 官方介绍 尽管深度学习已经彻底改变了计算机视觉&#xff0c;但目前的方法存在几个主要问题:典型的视觉数据集是劳动密集型的&#xff0c;创建成本高&#xff0c;同时只教授一组狭窄的视觉概念;标准视觉模型擅长于一项任务且仅擅长于一项任务&#xff0c;并且需要大…

【QT专栏】QT中实现多线程的四种方式总结(金针菇般细)

目录 一、继承QThread 1&#xff0c;基本概念 2&#xff0c;操作流程 二、继承QObject&#xff08;推荐&#xff09; 1&#xff0c;基本概念 2&#xff0c;操作流程 三、继承QRunnable&#xff0c;配合QThreadPool实现多线程 1&#xff0c;外界通信 2&#xff0c;QMet…

SpringSecurity的安全认证的详解说明(附完整代码)

SpringSecurity登录认证和请求过滤器以及安全配置详解说明 环境 系统环境&#xff1a;win10 Maven环境&#xff1a;apache-maven-3.8.6 JDK版本&#xff1a;1.8 SpringBoot版本&#xff1a;2.7.8 根据用户名密码登录 根据用户名和密码登录&#xff0c;登录成功后返回Token数据…

狂神聊Redis复习笔记一

目录目前一个基本的互联网项目&#xff01;NoSQL 特点Redis 是什么&#xff1f;Redis 能干嘛&#xff1f;特性测试性能基础的知识Redis 是单线程的&#xff01;Redis 为什么单线程还这么快&#xff1f;五大数据类型Redis-KeyString&#xff08;字符串&#xff09;List&#xff…

[软件工程导论(第六版)]第3章 需求分析(复习笔记)

文章目录3.1 需求分析的任务3.2 与用户沟通获取需求的方法3.3 分析建模与规格说明3.4 实体-联系图&#xff08;E-R图&#xff09;3.5 数据规范化3.6 状态转换图3.7 其他图形工具3.8 验证软件需求需求分析是软件定义时期的最后一个阶段&#xff0c;需求分析的基本任务是准确的回…

EASYui+C#web

第一步创建一个web应用程序。 选择web应用程序。 第二步选择mvc框架 创建完成项目目录。 如图引入easyui包。 记住复制到content文件夹&#xff0c;否则无法识别。 easyui下载&#xff0c;官网。 如何用 引入jscss文件 <link rel"stylesheet" type"text…

Guitar Pro8手机电脑免费版吉他软件下载

Guitar Pro8是专业的吉他软件&#xff0c;具有可视化的五线谱编辑器&#xff0c;涵盖常用的乐器和特殊乐器单元&#xff0c;内置海量吉他音色效果和1000多个乐器音色&#xff0c;成为一个小型音乐站&#xff0c;制作出动听的音乐&#xff0c;支持边看边听&#xff0c;添加音频轨…

【论文阅读】 Few-shot object detection via Feature Reweighting

Few-shot object detection的开山之作之一 ~~ 特征学习器使用来自具有足够样本的基本类的训练数据来 提取 可推广以检测新对象类的meta features。The reweighting module将新类别中的一些support examples转换为全局向量&#xff0c;该全局向量indicates meta features对于检…

使用MindSpore20.0的API快速实现深度学习模型之数据变换

文章目录前言一. 实验环境二. 安装ubuntu虚拟机2. 1.下载ubuntu镜像2.2 配置虚拟机2.3 安装操作系统三. 安装MindSpore20.0-alpha3.1 下载需要的安装程序脚本3.2 安装MindSpore 2.0.0-alpha和Python 3.73.3 开始手动安装3.4. 安装gcc3. 5.安装MindSpore3.6. 验证是否成功&#…