Visual Studio 高级调试-(上)

news2024/11/17 17:51:11

概述

编程圈子里隔三差五的就会有场“谁是最强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/354490.html

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

相关文章

MySQL ---基础概念

目录 餐前小饮&#xff1a;什么是服务器&#xff1f;什么是数据库服务器&#xff1f; 一、数据库服务软件 1. 常见数据库产品 2.如何开启和停止MySQL服务 二、数据库术语及语法 1.数据库术语 2.SQL语法结构 3.SQL 语法要点 三、SQL分类 1.数据定义语言&#xff08;D…

LeetCode——1237. 找出给定方程的正整数解

一、题目 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/find-positive-integer-solution-for-a-given-equation/description/ 翻译一下题目 意思是&#xff0c;这是一个二维单调递增的函数&#xff0c;函数一共有 9 …

快速上手GoWeb开发之Gin框架

Go是一门正在快速增长的编程语言&#xff0c;专为构建简单、快速且可靠的软件而设计。 golang提供的net/htp库已经很好了&#xff0c;对于htp的协议的实现非常好&#xff0c;基于此再造框架&#xff0c;也不会是难事&#xff0c;因此生态中出现了很多框架。 Gin: Go 语言编写的…

智能洗地机什么牌子好?智能洗地机排行

要说家庭清洁热门的产品&#xff0c;洗地机肯定首当其冲&#xff0c;集洗吸拖为一体的清洁工具&#xff0c;省时又省力&#xff0c;可谓是家里清洁好助手&#xff0c;今天笔者就为大家介绍几款重量轻、噪音轻、拖地干净的洗地机! 一、CEYEE希亦T800洗地机 在国内&#xff0c;洗…

13.进程---

一、进程的概念 1.可以用ps或top查看进程 2.pid0;交换进程&#xff08;作用是进程调度&#xff09; pid1;init进程&#xff08;作用是系统初始化&#xff09; 3.getpid();//获取自身的进程标识符 4.getppid();//获取父进程的进程标识符 什么是父进程&#xff1f; 答&#x…

【GStreamer 】 TX1中CPU和GPU解码显示海康相机RTSP流

大家好&#xff0c;我是虎哥&#xff0c;今天找了一套海康的相机&#xff0c;想后续测试一下DeepStream用网络相机RTSP流做输入看看后续目标识别和分类。但是还是想先实时看看视频&#xff0c;当然&#xff0c;可以选择VLC去查看&#xff0c;顺道我也用GStreamer 来测试了一下&…

HashMap原理详解

一、hashmap简介 hashmap是Java当中一种数据结构&#xff0c;是一个用于存储Key-Value键值对的集合&#xff0c;每一个键值对也叫作Entry。 二、JDK7的HashMap1、JDK7时HashMap的数据结构 1、在JDK7之前&#xff0c;hashmap底层采用数组链表的数据结构来存储数据 2、插入数据采…

SpringBoot静态资源访问

静态资源路径 类路径下&#xff1a;/resources/static/、/resources/public/、/resources/resources/、/resources/META-INF/resources 这些路径下的资源均可直接访问&#xff1b;通过 http://ip:port/资源名称 访问即可 可在配置文件中对访问路径和访问拦截规则进行设置&…

【ArcGIS Pro二次开发】(5):UI管理_自定义控件的位置

新增的自定义控件一般放在默认的【加载项】选项卡下&#xff0c;但是根据需求&#xff0c;我们可能需要将控件放在新的自定义选项卡下&#xff0c;在自定义选项卡添加系统自带的控件&#xff0c;将自定义的按钮等控件放在右键菜单栏里以方便使用&#xff0c;等等。 下面就以一…

Maven:工程的拆分与聚合

Maven 拆分与聚合创建父工程创建子模块pom.xml配置示例拆分与聚合 在 Maven 中, 拆分是将一个完整的项目分成一个个独立的小模块,聚合是将各个模块进一步组合,形成一个完整的项目。接下来简单示例拆分与聚合的过程。 创建父工程 父工程,一个pom工程,目录结构简单,只需有…

cocos2dx 4.0 - cpp - pc版 环境搭建

开发环境vs2022 cocos2dx4.0 python2.7.18 cmake3.25安装教程&#xff08;环境搭建&#xff09;安装VS2022-Community&#xff0c; 勾选c进行安装安装cmake3.25, 勾选环境变量进行安装安装python2.7.18, 勾选环境变量进行安装下载cocos2dx4.0并解压配置cocos2dx:运行cmd,进入…

DCL 数据控制语言

1、简介 DCL英文全称是Data Control Language(数据控制语言)&#xff0c;用来管理数据库用户、控制数据库的访问权限。 2、管理用户 2.1 查询用户 select * from mysql.user;查询的结果如下: 其中 Host代表当前用户访问的主机, 如果为localhost, 仅代表只能够在当前本机访问…

Nuxtjs

SSR渲染 SSR&#xff1a;在服务器端将Vue渲染成HTML返回给浏览器 优点 对SEO支持好SPA单页渲染更快 npx create-nuxt-app nuxtDemo 修改开发地址 package.json文件中进行修改 "config":{"nuxt":{"host":"127.0.0.1","port…

朋友当上项目测试组长了,我真的羡慕了

最近我发现一个神奇的事情&#xff0c;我一个朋友居然已经当上了测试项目组长&#xff0c;据我所知他去年还是在深圳的一家创业公司做苦逼的测试狗&#xff0c;短短8个月&#xff0c;到底发生了什么&#xff1f; 于是我立刻私聊他八卦一番。 原来他所在的公司最近正在裁员&am…

01_Docker 简介

01_Docker 简介 文章目录01_Docker 简介1.1 Docker 简介1.2 Docker 组件1.2.1 Docker 客户端和服务区1.2.2 Docker 镜像1.2.3 Registry1.2.4 Docker 容器参考资料https://www.runoob.com/docker/ubuntu-docker-install.html 1.1 Docker 简介 Docker 是一个能够把开发的应用程…

HTML+CSS+JavaScript学习笔记~ 从入门到精通!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、HTML1. 什么是HTML&#xff1f;一个完整的页面&#xff1a;<!DOCTYPE> 声明中文编码2.HTML基础①标签头部元素标题段落注释水平线文本格式化②属性3.H…

SOFA Weekly|开源人、本周贡献 issue 精选

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展欢迎留言互动&#xff5e;SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&#…

Docker安装Tomcat、mysql、redis

目录 前言 一、安装Tomcat 二、安装mysql &#xff08;一&#xff09;简单版 &#xff08;二&#xff09;实战版 三、安装redis 前言 镜像可以先去Docker Hub Container Image Library | App Containerization 左上角搜&#xff0c;然后点进入可以看到具体的命令&#…

网络协议(TCP/IP)

目录一、网络分层模型二、OSI模型三、网络传输原理四、TCP/IP1、TCP/IP 原理2、TCP 三次握手/四次挥手3、Http协议和TCP/IP的区别五、HTTP原理六、HTTPS原理七、CDN原理一、网络分层模型 互联网的本质就是一系列的网络协议&#xff0c;最早由ISO国际组织定义为7层网络参考模型…

【JS】数组常用方法总结-功能、参数、返回值

数组常用方法总结-功能、参数、返回值 用简单的js示例 运行在线工具&#xff1a;链接: 菜鸟工具 菜鸟工具示意图&#xff1a; ![在这里插入图片描述](https://img-blog.csdnimg.cn/de8589eb1acf42abb0347d8a3a3bbdfa.png 1.会改变原有数组方法 &#xff08;1&#xff09;pu…