Net 高级调试之五:如何在托管函数上设置断点
一、简介
今天是《Net 高级调试》的第五篇文章。今天这篇文章开始介绍如何在托管方法和非托管方法设置断点,我们要想调试程序,必须掌握调试的一些命令,动态调试的命令,我们在上一篇文章已经讲过了。光有命令也是不行的,要让这些调试命令有用,必须可以在方法上设置断点,然后,再使用调试命令,才能完成我们的调试任务。当然了,第一次看视频或者看书,是很迷糊的,不知道如何操作,还是那句老话,一遍不行,那就再来一遍,还不行,那就再来一遍,俗话说的好,书读千遍,其意自现,我这是第三遍。
如果在没有说明的情况下,所有代码的测试环境都是 Net Framewok 4.8,但是,有时候为了查看源码,可能需要使用 Net Core 的项目,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。
调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
操作系统:Windows Professional 10
调试工具:Windbg Preview(可以去Microsoft Store 去下载)
开发工具:Visual Studio 2022
Net 版本:Net Framework 4.8
CoreCLR源码:源码下载
二、基础知识
1、非托管函数下断点
其实对非托管函数下断点是十分方便的,因为C/C++函数在编译之后就成了【机器代码】了,函数名就进入了【符号表】,比如我们可以非常方便的 notepad 的 SaveFile 函数下断点。
操作步骤如下:
a、打开 notepad 。
b、使用 x notepad!SaveFile 下断点。
c、在 notepad 上保存一下文件,就会触发断点。
2、托管函数下断点
2.1、简介
托管函数下断点是很难的,因为你要下断点的方法的机器码在内存中可能还没有生成,也就是 JIT 从来就没有对该方法进行编译过,所以在还没有生成的代码上下断点就比较麻烦。虽然比较麻烦,并不代表不能实现,我们还是有三种方法可以对托管函数下断点的。
2.2、托管函数下断点的三种方式
1)、在编译后的函数上下断点
这是最简单的一种方式,既然方法已经编译完成,肯定就已经生成了机器代码,那在编译后的函数上下断点就容易很多了,和非托管的是一样的。
2)、在未编译的函数上下断点
a、使用 !bpmd assembly.exe(模块包含后缀名) namespace.ClassName.MethodName
b、使用 sosex 扩展的 mbm 下断点(只能在 Net framework 下使用,Net Core 是不支持的)。
3)、对泛型方法下断点
如果我们想对泛型类型的方法下断点,最首要的任务就是找到泛型类型的名称和方法的名称,找到之后,我们就可以下断点了。找泛型类型的名称和方法的名称有两种办法,第一种是通过命令,第二种是我们可以使用 ILSpy 找到。
三、调试过程
废话不多说,这一节是具体的调试操作的过程,又可以说是眼见为实的过程,在开始之前,我还是要啰嗦两句,这一节分为两个部分,第一部分是测试的源码部分,没有代码,当然就谈不上测试了,调试必须有载体。第二部分就是根据具体的代码来证实我们学到的知识,是具体的眼见为实。
1、测试源码
1.1、Example_5_1_1

1 namespace Example_5_1_1
2 {
3 internal class Program
4 {
5 static void Main(string[] args)
6 {
7 var sum = Sum(10, 20);
8
9 Debugger.Break();
10
11 sum = Sum(100, 200);
12
13 Console.WriteLine($"sum={sum}");
14 }
15
16 private static int Sum(int a, int b)
17 {
18 var sum = a + b;
19
20 return sum;
21 }
22 }
23 }
1.2、Example_5_1_2

1 namespace Example_5_1_2
2 {
3 internal class Program
4 {
5 static void Main(string[] args)
6 {
7 Console.WriteLine("请查看:未编译形态的 Sum 方法");
8 Debugger.Break();
9
10 var sum = Sum(10,20);
11 Console.WriteLine("请查看:已经编译形态的 Sum 方法");
12 Debugger.Break();
13
14 sum = Sum(100, 200);
15
16 Console.WriteLine($"sum={sum}");
17 }
18
19 private static int Sum(int a, int b)
20 {
21 var sum = a + b;
22
23 return sum;
24 }
25 }
26 }
1.3、Example_5_1_3

1 namespace Example_5_1_3
2 {
3 internal class Program
4 {
5 static void Main(string[] args)
6 {
7 Debugger.Break();
8
9 var mylist = new MyList<int>();
10
11 mylist.Add(10);
12
13 Console.ReadLine();
14 }
15 }
16
17 public class MyList<T>
18 {
19 public T[] arr = new T[10];
20
21 public void Add(T t)
22 {
23 arr[0] = t;
24 }
25 }
26 }
2、眼见为实
2.1、在非托管函数 Notepad 的 SaveFile 方法上下断点。
测试程序:Notepad
操作流程:我们先要打开 notepad,然后再打开 windbg,点击【文件】菜单,然后通过【attach to process】附加进程,最后点击【Attach】按钮完成附加进程的操作。现在 notepad 是不能操作的,因为断点断住了,所以我们执行【g】命令,运行一下。然后我们点击工具栏【break】按钮,中断,然后就可以调试了。
我们使用【x】命令查找一下 notepad 的 SaveFile 方法。
1 0:002> x notepad!*SaveFile*
2 00007ff6`e46be780 notepad!SaveFile (bool __cdecl SaveFile(struct HWND__ *,class wil::unique_any_t<class wil::details::unique_storage<struct wil::details::resource_policy<unsigned short *,void (__cdecl*)(void *),&void __cdecl CoTaskMemFree(void *),struct wistd::integral_constant<unsigned __int64,0>,unsigned short *,unsigned short *,0,std::nullptr_t> > > &,bool,unsigned short const *))
3 00007ff6`e46d508c notepad!_imp_load_GetSaveFileNameW (__imp_load_GetSaveFileNameW)
4 00007ff6`e46e50b0 notepad!_imp_GetSaveFileNameW = <no type information>
使用【bp】命令对 notepad!SaveFile 函数下断点。下断点后,继续运行,使用【g】命令。
1 0:002> bp notepad!SaveFile
2 0:002> g
此时,我们可以操作 notepad,随便输入一些内容,然后点击【保存】,就可以被断点断住。
1 .......
2 Breakpoint 0 hit
3 notepad!SaveFile:
4 00007ff6`e46be780 488bc4 mov rax,rsp
我们可以使用【k】命令显示调用栈也能说明问题。
1 0:000> k
2 # Child-SP RetAddr Call Site
3 00 00000002`1a1dec58 00007ff6`e46b9336 notepad!SaveFile
4 01 00000002`1a1dec60 00007ff6`e46badf4 notepad!NPCommand+0x2d2
5 02 00000002`1a1df240 00007ff8`23e0e338 notepad!NPWndProc+0x844
6 03 00000002`1a1df570 00007ff8`23e0dd79 USER32!UserCallWinProcCheckWow+0x2f8
7 04 00000002`1a1df700 00007ff6`e46bb30c USER32!DispatchMessageWorker+0x249
8 05 00000002`1a1df780 00007ff6`e46d3b66 notepad!wWinMain+0x29c
9 06 00000002`1a1df830 00007ff8`23b86fd4 notepad!__scrt_common_main_seh+0x106
10 07 00000002`1a1df870 00007ff8`25bbcec1 KERNEL32!BaseThreadInitThunk+0x14
11 08 00000002`1a1df8a0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
截图效果:

2.2、在已经编译的托管函数上下断点。
测试程序:Example_5_1_1
我们使用 Windbg Preview 调试器,通过【launch executable】菜单加载【Example_5_1_1.exe】项目,通过【g】命令,运行程序,调试器运行代【Debugger.Break()】次会暂停执行。当然,我们可以使用【cls】命令清理一下调试器显示的过多信息,自己来决定,我是会清理的。我们将在【sum = Sum(100, 200);】这行下断点。
当调试器暂停的时候,说明有一部分代码已经执行了。【var sum = Sum(10, 20);】就是这行代码已经被执行,也可以说是被 JIT 编译了。我们如何查看 Sum 方法被编译的代码呢?可以使用【!name2ee】命令。
1 0:000> !name2ee Example_5_1_1!Example_5_1_1.Program.Sum
2 Module: 00fc4044
3 Assembly: Example_5_1_1.exe
4 Token: 06000002
5 MethodDesc: 00fc4d64
6 Name: Example_5_1_1.Program.Sum(Int32, Int32)
7 JITTED Code Address: 01010908
红色标注表示代码已经编译,我们也可以使用【!u】命令查看这个代码的汇编代码,汇编代码很多,所以折叠。

1 0:000> !u 01010908
2 Normal JIT generated code
3 Example_5_1_1.Program.Sum(Int32, Int32)
4 Begin 01010908, size 3e
5
6 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_1\Program.cs @ 20:
7 >>> 01010908 55 push ebp
8 01010909 8bec mov ebp,esp
9 0101090b 83ec10 sub esp,10h
10 0101090e 894dfc mov dword ptr [ebp-4],ecx
11 01010911 8955f8 mov dword ptr [ebp-8],edx
12 01010914 833df042fc0000 cmp dword ptr ds:[0FC42F0h],0
13 0101091b 7405 je 01010922
14 0101091d e85ef40670 call clr!JIT_DbgIsJustMyCode (7107fd80)
15 01010922 33d2 xor edx,edx
16 01010924 8955f0 mov dword ptr [ebp-10h],edx
17 01010927 33d2 xor edx,edx
18 01010929 8955f4 mov dword ptr [ebp-0Ch],edx
19 0101092c 90 nop
20
21 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_1\Program.cs @ 21:
22 0101092d 8b45fc mov eax,dword ptr [ebp-4]
23 01010930 0345f8 add eax,dword ptr [ebp-8]
24 01010933 8945f4 mov dword ptr [ebp-0Ch],eax
25
26 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_1\Program.cs @ 23:
27 01010936 8b45f4 mov eax,dword ptr [ebp-0Ch]
28 01010939 8945f0 mov dword ptr [ebp-10h],eax
29 0101093c 90 nop
30 0101093d eb00 jmp 0101093f
31
32 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_1\Program.cs @ 24:
33 0101093f 8b45f0 mov eax,dword ptr [ebp-10h]
34 01010942 8be5 mov esp,ebp
35 01010944 5d pop ebp
36 01010945 c3 ret

我们使用【bp】命令在地址【01010908】地址处下断点,【g】继续运行,就会在 Sum 方法的第一行断住。
0:000> bp 01010908
继续运行后,
1 0:000> g
2 Breakpoint 0 hit
3 eax=00000000 ebx=00aff108 ecx=00000064 edx=000000c8 esi=02b524bc edi=00aff058
4 eip=01010908 esp=00aff03c ebp=00aff068 iopl=0 nv up ei pl zr na pe nc
5 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
6 Example_5_1_1!COM+_Entry_Point <PERF> (Example_5_1_1+0x920908):
7 01010908 55 push ebp
效果截图:

2.3、在未编译的托管函数上下断点。
测试程序:Example_5_1_2
我们使用 Windbg Preview 调试器,通过【launch executable】菜单加载【Example_5_1_2.exe】项目,通过【g】命令,运行程序,调试器运行代【Debugger.Break()】次会暂停执行,此行代码在12。当然,我们可以使用【cls】命令清理一下调试器显示的过多信息,自己来决定,我是会清理的。这次我们的任务依然是在 Sum 方法上下断点。
先来一个截图吧,效果更明显:

我们还是先使用【!name2ee】命令查找一下 Sum 方法。
1 0:000> !name2ee Example_5_1_2!Example_5_1_2.Program.Sum
2 Module: 00f34044
3 Assembly: Example_5_1_2.exe
4 Token: 06000002
5 MethodDesc: 00f34d64
6 Name: Example_5_1_2.Program.Sum(Int32, Int32)
7 Not JITTED yet. Use !bpmd -md 00f34d64 to break on run.
红色标注说明,我们的代码还没有被JIT编译。我们听从他的建议,使用【!bpmd -md】下断点。
1 0:000> !bpmd -md 00f34d64
2 MethodDesc = 00f34d64
3 Adding pending breakpoints...
我们可以使用【g】命令,继续运行,果然在断点处暂停。我这里是可以的,但是视频说是不可以的。
1 0:000> g
2 (bb0.3798): CLR notification exception - code e0444143 (first chance)
3 JITTED Example_5_1_2!Example_5_1_2.Program.Sum(Int32, Int32)
4 Setting breakpoint: bp 00F8094C [Example_5_1_2.Program.Sum(Int32, Int32)]
5 Breakpoint: JIT notification received for method Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 0100da40.
6 Breakpoint 0 hit
7 eax=00f80928 ebx=009ef098 ecx=0000000a edx=00000000 esi=02c224bc edi=009eefe8
8 eip=00f8094c esp=009eefb8 ebp=009eefc8 iopl=0 nv up ei pl zr na pe nc
9 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
10 Example_5_1_2!COM+_Entry_Point <PERF> (Example_5_1_2+0x73094c):
11 00f8094c 90 nop
截图效果:

1)、使用 !bpmd moduleName namespace.ClassName.MethodName
这个实现的原理是借助 JIT 的编译完成通知事件,在事件中判断当前编译的是否是我们下断点的方法,如果是就转成 bp 命令下断点,从输出的信息也可以看得出来。
【!bpmd】命令格式一下两种都对:!bpmd Example_5_1_2.exe Example_5_1_2.Program.Sum 或者 !bpmd Example_5_1_2 Example_5_1_2.Program.Sum
1 0:000> !bpmd Example_5_1_2 Example_5_1_2.Program.Sum
2 Found 1 methods in module 011a4044...
3 MethodDesc = 011a4d64
4 Adding pending breakpoints...
断点设置成功,【g】继续运行,成功在断点处暂停。
1 0:000> g
2 (14c4.1b60): CLR notification exception - code e0444143 (first chance)(表示 JIT 已经编译好了,CLR 给 Windbg 发出一个异常通知)
3 JITTED Example_5_1_2!Example_5_1_2.Program.Sum(Int32, Int32)
4 Setting breakpoint: bp 02DD094C [Example_5_1_2.Program.Sum(Int32, Int32)](Windbg 拿到编译后的机器码地址,重新设置断点)
5 Breakpoint: JIT notification received for method Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 011f0fe8.
6 Breakpoint 0 hit
7 eax=02dd0928 ebx=00efedd8 ecx=0000000a edx=00000000 esi=02f124bc edi=00efed28
8 eip=02dd094c esp=00efecf8 ebp=00efed08 iopl=0 nv up ei pl zr na pe nc
9 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
10 Example_5_1_2!COM+_Entry_Point <PERF> (Example_5_1_2+0x22d094c):
11 02dd094c 90 nop
截图效果:

2)、使用 sosex 扩展的 mbm 下断点(只能在 Net framework 下使用,Net Core 是不支持的)。
mbm 是非托管命令 bm 的托管版本,对方法名下断点,还支持模糊匹配。
1 0:000> !mbm Example_5_1_2!Example_5_1_2.Program.Sum
设置断点后,我们可以继续运行,执行【g】命令。
1 0:000> g
2 Breakpoint: JIT notification received for method Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 00a61010.
3 Breakpoint set at Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 00a61010.
4 Breakpoint 5 hit
5 eax=00a20928 ebx=008ff050 ecx=0000000a edx=00000000 esi=029d24bc edi=008fef98
6 eip=00a2094c esp=008fef68 ebp=008fef78 iopl=0 nv up ei pl zr na pe nc
7 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
8 Example_5_1_2!COM+_Entry_Point <PERF> (Example_5_1_2+0x5a094c):
9 00a2094c 90 nop
成功断住,效果如图:

【mbm】命令很强大,也可以支持模糊查找。这个操作是另外一个过程,需要重新运行调试程序。
1 0:000> !mbm Example_5_1_2!*Sum
成功在断点出暂停。
1 0:000> g
2 Breakpoint: JIT notification received for method Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 01720fb8.
3 Breakpoint set at Example_5_1_2.Program.Sum(Int32, Int32) in AppDomain 01720fb8.
4 Breakpoint 1 hit
5 eax=031c0928 ebx=0137f378 ecx=0000000a edx=00000000 esi=033d24bc edi=0137f2c8
6 eip=031c094c esp=0137f298 ebp=0137f2a8 iopl=0 nv up ei pl zr na pe nc
7 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
8 Example_5_1_2!COM+_Entry_Point <PERF> (Example_5_1_2+0x21e094c):
9 031c094c 90 nop
2.4、在泛型方法上下断点。
测试程序:Example_5_1_3
我们使用 Windbg Preview 调试器,通过【launch executable】菜单加载【Example_5_1_3.exe】项目,通过【g】命令,运行程序,调试器运行代【Debugger.Break();】次会暂停执行。当然,我们可以使用【cls】命令清理一下调试器显示的过多信息,自己来决定,我是会清理的。
这次的任务是,我们要在泛型类型 MyList<T> 的 Add() 方法上下断点。
我们想要在泛型类型的方法上下断点,首要的任务是找到泛型类型的名称和方法的名称,这是关键。
效果如图:

a、我们通过 Windbg 和 SOS 的命令找到类型的名称。
编译程序集后,泛型类型一定在这个程序集的模块中。然后我们再在这个模块中打印出所有的类型,就可以找到这个类型了。
我们现在这个程序集中查找模块信息,我们可以使用【!dumpdomain】命令。
1 0:000> !dumpdomain
2 --------------------------------------
3 System Domain: 7141caf8
4 ....
5 --------------------------------------
6 Shared Domain: 7141c7a8
7 ......
8
9 --------------------------------------
10 Domain 1: 00a2da30
11 ......
12
13 Assembly: 00a87ea8 [E:\Visual Studio 2022\Source\Projects\......\Example_5_1_3\bin\Debug\Example_5_1_3.exe]
14 ClassLoader: 00a873a8
15 SecurityDescriptor: 00a872a0
16 Module Name
17 01004044(模块地址) E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_5_1_3\bin\Debug\Example_5_1_3.exe
我们找到了模块,就可以将模块中所有的类型输出出来,可以使用【!dumpmodule -mt 】命令。
1 0:000> !dumpmodule -mt 01004044
2 Name: E:\Visual Studio 2022\Source\Projects\...\Example_5_1_3\bin\Debug\Example_5_1_3.exe
3 Attributes: PEFile
4 Assembly: 00a87ea8
5 LoaderHeap: 00000000
6 TypeDefToMethodTableMap: 01000038
7 TypeRefToMethodTableMap: 01000048
8 MethodDefToDescMap: 01000094
9 FieldDefToDescMap: 010000a8
10 MemberRefToDescMap: 00000000
11 FileReferencesMap: 010000b8
12 AssemblyReferencesMap: 010000bc
13 MetaData start address: 006220a8 (1680 bytes)
14
15 Types defined in this module
16
17 MT TypeDef Name
18 ------------------------------------------------------------------------------
19 01004d6c 0x02000002 Example_5_1_3.Program
20 01004de8 0x02000003 Example_5_1_3.MyList`1
21
22 Types referenced in this module
23
24 MT TypeRef Name
25 ------------------------------------------------------------------------------
26 6f802734 0x02000010 System.Object
27 6f847540 0x02000011 System.Diagnostics.Debugger
28 6f808af0 0x02000012 System.Console
红色标注的就是我们要查找泛型类型真实的名称。有了类型,我们继续可以使用【!dumpmt -md ...】命令,输出它所有方法。
1 0:000> !dumpmt -md 01004de8
2 EEClass: 01001334
3 Module: 01004044
4 Name: Example_5_1_3.MyList`1
5 mdToken: 02000003
6 File: E:\Visual Studio 2022\Source\Projects\......\Example_5_1_3\bin\Debug\Example_5_1_3.exe
7 BaseSize: 0xc
8 ComponentSize: 0x0
9 Slots in VTable: 6
10 Number of IFaces in IFaceMap: 0
11 --------------------------------------
12 MethodDesc Table
13 Entry MethodDe JIT Name
14 6fbf97b8 6f7fc838 PreJIT System.Object.ToString()
15 6fbf96a0 6f938978 PreJIT System.Object.Equals(System.Object)
16 6fc021f0 6f938998 PreJIT System.Object.GetHashCode()
17 6fbb4f2c 6f9389a0 PreJIT System.Object.Finalize()
18 02840458 01004dd4 NONE Example_5_1_3.MyList`1..ctor()
19 02840450 01004dcc NONE Example_5_1_3.MyList`1.Add(!0)
红色标记就是我们要查找的 Add 方法,有了方法的地址,我们就可以使用【bpmd】命令为其下断点了。
1 0:000> !bpmd Example_5_1_3 Example_5_1_3.MyList`1.Add
2 Found 1 methods in module 01004044...
3 MethodDesc = 01004dcc
4 Adding pending breakpoints...
断点设置成功后,我们使用【g】命令,程序继续运行,就可以在断点处暂停。
1 0:000> g
2 (3ab4.2920): CLR notification exception - code e0444143 (first chance)
3 JITTED Example_5_1_3!Example_5_1_3.MyList`1[[System.Int32, mscorlib]].Add(Int32)
4 Setting breakpoint: bp 02840942 [Example_5_1_3.MyList`1[[System.Int32, mscorlib]].Add(Int32)]
5 Breakpoint: JIT notification received for method Example_5_1_3.MyList`1[[System.Int32, mscorlib]].Add(Int32) in AppDomain 00a2da30.
6 Breakpoint 0 hit
7 eax=02840928 ebx=007bee20 ecx=029f26b0 edx=0000000a esi=00000000 edi=007bed90
8 eip=02840942 esp=007bed58 ebp=007bed60 iopl=0 nv up ei pl zr na pe nc
9 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
10 Example_5_1_3!COM+_Entry_Point <PERF> (Example_5_1_3+0x2220942):
11 02840942 90 nop
断点效果如图:

b、我们可以使用 ILSpy 来查找泛型类型的名称和方法的名称。
效果如图:

上面就是类型的名称,我继续查找方法的名称,也很简单。
效果如图:

有了这些信息,我们就可以使用 Windbg 为程序设置断点了,操作过程和 a 的过程一样,就不多说了。
四、总结
终于写完了,为什么说是终于,因为写这一篇文章,不是一天完成的。写文章,记录操作过程,作图例,所以时间就长了。今天介绍的是如何在方法上设置断点,有了断点,我们就可以使用上一篇讲的动态调试命令,我们就可以更容易完成调试任务,掌握这些调试技巧还是很有必要的。好了,不说了,不忘初心,继续努力,希望老天不要辜负努力的人。
Net 高级调试之五:如何在托管函数上设置断点的更多相关文章
- Lua中如何实现类似gdb的断点调试—07支持通过函数名称添加断点
我们之前已经支持了通过函数来添加断点,并且已经支持了行号的检查和自动修正.但是通过函数来添加断点有一些限制,如果在当前的位置无法访问目标函数,那我们就无法对其添加断点. 于是,本篇我们将扩展断点设置的 ...
- Linux高级编程--04.GDB调试程序(设置断点)
调试已运行的程序 在UNIX下用ps查看正在运行的程序的PID(进程ID),然后用gdb PID格式挂接正在运行的程序. 先用gdb 关联上源代码,并进行gdb,在gdb中用attach命令来挂接进程 ...
- ###Android 断点调试和高级调试###
转自:http://www.2cto.com/kf/201506/408358.html 有人说Android 的调试是最坑的,那我只能说是你不会用而已,我可以说Android Studio的调试是我 ...
- Linux高级调试与优化——gdb调试命令
番外 2019年7月26日至27日,公司邀请<软件调试>和<格蠹汇编——软件调试案例集锦>两本书的作者张银奎老师进行<Linux高级调试与优化>培训,有幸聆听张老师 ...
- Visual Studio高级调试技巧
1. 设置软件断点,运行到目标位置启动调试器 方法①:使用汇编指令(注:x64 c++不支持嵌入汇编) _asm 方法②:编译器提供的方法 __debugbreak(); 方法③:使用windows ...
- [Android Studio 权威教程]断点调试和高级调试
好了开始写一个简单的调试程序,我们先来一个for循环 ? 1 2 3 4 5 6 7 8 <code class="language-java hljs ">for ( ...
- GDB高级调试
一.多线程调试 多线程调试可能是问得最多的.其实,重要就是下面几个命令: info thread 查看当前进程的线程. thread <ID> 切换调试的线程为指定ID的线程. break ...
- python高级(六)——用一等函数实现设计模式
本文主要内容 经典的“策略”模式 python高级——目录 文中代码均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高级 ...
- Android Stuido中断点调试和高级调试
写一个简单的调试程序 import android.os.Bundle; import android.support.v7.app.AppCompatActivity; public class M ...
- .NET高级调试系列-Windbg调试入门篇
Windbg是.NET高级调试领域中不可或缺的一个工具和利器,也是日常我们分析解决问题的必备.准备近期写2篇精华文章,集中给大家分享一下如果通过Windbg进行.NET高级调试. 今天我们来一篇入门的 ...
随机推荐
- 聊聊又拍云存储 S3 协议的使用
近期,有细心的同学发现,在又拍云控制台中的云存储产品中增加了针对 S3 协议标准的兼容支持,授权用户通过 S3 协议标准对存储空间的数据进行读写操作.此配置操作之前是由人工协助的方式提供给用户使用的, ...
- Java通用返回工具类Result
通用返回类Result 前言:Java项目搭建时,常常需要去封装一个通用型的Result工具类,下面就是我自己封装的常用的返回类,可以直接使用.(有部分Swagger注解,使用时可忽略) 第一步.创建 ...
- Linux 安装:中文manpages
Linux 中文man手册安装 bash 脚本 wget https://src.fedoraproject.org/repo/pkgs/man-pages-zh-CN/manpages-zh-1.5 ...
- 用Claude-2-100K复刻了胡锡进老师的写作风格
大家好,我是老章 最近玩心大发,用Claude-2-100K复刻了胡锡进老师的写作风格,然后用这个风格点评世间万物. 蛮有意思的,直接看效果吧: 怎么实现的呢?老章不喜欢废话,极简介绍一下步骤. 第一 ...
- npm install 下载依赖的过程
首先检查.npmrc文件,项目级.npmrc文件>用户级的.npmrc文件>全局性的.npmrc文件>npm内置的.npmrc文件 是否有lock文件 没有lock文件 从npm远程 ...
- go创建web项目分别在windows和linux部署
转载请注明出处: 要在Linux服务器上运行Go的Web项目,可以按照以下步骤进行操作: 在服务器上安装Go:首先,在Linux服务器上安装Go编程语言.你可以从官方网站(https://golang ...
- 《代码整洁之道 Clean Code》学习笔记 Part 1
前段时间在看<架构整洁之道>,里面提到了:构建一个好的软件系统,应该从写整洁代码做起.毕竟,如果建筑使用的砖头质量不佳,再好的架构也无法造就高质量的建筑.趁热打铁,翻出<代码整洁之道 ...
- 史上最强.NET数据分页方法
[前言] 本文讲述的.NET数据分页方法为[史上最强],已被多家大型科技公司实战采用 & 也被圈内多家知名IT培训机构转载收藏. [正文] 支持.Net Core(2.0及以上)与.Net F ...
- python如何提取浏览器中保存的网站登录用户名密码
python如何提取Chrome中的保存的网站登录用户名密码? 很多浏览器都贴心地提供了保存用户密码功能,用户一旦开启,就不需要每次都输入用户名.密码,非常方便.作为python脚本,能否拿到用户提前 ...
- Mysql高阶自定义排序
Mysql高阶自定义排序 嗨,大家好,我是远码,隔三岔五给大家分享一点工作的技术总结,花费的时间不多,几分钟就行,谢谢! Mysql对我们码农来说是在熟悉不过的日常了,就不在介绍它的基础用法了,今天我 ...