关于怎么样使用LLDB调节和测试CoreCL牧马人的介绍能够看,关于什么运用LLDB调节和测试CoreCL库罗德的牵线能够看

在这一篇我将使用LLDB实际跟踪CoreCLR中GC,在这一篇我将使用LLDB实际跟踪CoreCLR中GC

在上一篇中本人分析了CoreCL路虎极光中GC的中间处理,
在这壹篇小编将运用LLDB实际追踪CoreCLR中GC,关于怎么样利用LLDB调节和测试CoreCL奥迪Q5的牵线能够看:

在上1篇中自个儿分析了CoreCL帕Jero中GC的里边处理,
在这一篇作者将接纳LLDB实际追踪CoreCLPAJERO中GC,关于如何使用LLDB调节和测试CoreCLBMWX伍的牵线能够看:

  • 微软官方的文书档案,地址
  • 本人在第①篇中的介绍,地址
  • LLDB官方的入门文书档案,地址
  • 微软官方的文档,地址
  • 本身在第二篇中的介绍,地址
  • LLDB官方的入门文书档案,地址

源代码

本篇跟踪程序的源代码如下:

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication
{
    public class Program
    {
        public class ClassA { }
        public class ClassB { }
        public class ClassC { }

        public static void Main(string[] args)
        {
            var a = new ClassA();
            { var b = new ClassB(); }
            var c = new ClassC();

            GCHandle handle = GCHandle.Alloc(c, GCHandleType.Pinned);
            IntPtr address = handle.AddrOfPinnedObject();
            Console.WriteLine((long)address);

            GC.Collect();
            Console.WriteLine("first collect completed");

            c = null;
            GC.Collect();
            Console.WriteLine("second collect completed");

            GC.Collect();
            Console.WriteLine("third collect completed");
        }
    }
}

源代码

本篇追踪程序的源代码如下:

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication
{
    public class Program
    {
        public class ClassA { }
        public class ClassB { }
        public class ClassC { }

        public static void Main(string[] args)
        {
            var a = new ClassA();
            { var b = new ClassB(); }
            var c = new ClassC();

            GCHandle handle = GCHandle.Alloc(c, GCHandleType.Pinned);
            IntPtr address = handle.AddrOfPinnedObject();
            Console.WriteLine((long)address);

            GC.Collect();
            Console.WriteLine("first collect completed");

            c = null;
            GC.Collect();
            Console.WriteLine("second collect completed");

            GC.Collect();
            Console.WriteLine("third collect completed");
        }
    }
}

安不忘危调节和测试

条件和作者的第1篇小说同样,都以ubuntu 1陆.04 LTS,首先供给揭露程序:

dotnet publish

发表程序后,把团结编译的coreclr文件覆盖到发表目录中:
复制coreclr/bin/Product/Linux.x64.Debug下的文本到程序目录/bin/Debug/netcoreapp1.1/ubuntu.16.04-x64/publish下。
请不要设置开启服务器GC,一来是那篇小说分析的是工作站GC的拍卖,贰来开启服务器GC很轻松产生调节和测试时死锁。

准备调节和测试

条件和自家的第二篇小说同样,都以ubuntu 1陆.0四 LTS,首先要求披露程序:

dotnet publish

发表程序后,把本人编写翻译的coreclr文件覆盖到公布目录中:
复制coreclr/bin/Product/Linux.x64.Debug下的文本到程序目录/bin/Debug/netcoreapp1.1/ubuntu.16.04-x64/publish下。
请不要设置开启服务器GC,一来是那篇作品分析的是职业站GC的拍卖,二来开启服务器GC很轻松产生调节和测试时死锁。

进去调节和测试

准备干活到位之后就能够进来调剂了

cd 程序目录/bin/Debug/netcoreapp1.1/ubuntu.16.04-x64/publish
lldb-3.6 程序名称

图片 1

第2设置gc主函数的断点,然后运维程序

b gc1
r

图片 2

大家停在了gc1函数,未来得以用bt来看调用来源

图片 3

此番是手动触发GC,调用来源中隐含了GCInterface::Collect和JIT生成的函数

必要呈现当前的地头变量能够用fr v,须要打字与印刷变量只怕表明式能够用p

图片 4

现在用n来步过,用s来步进继续追踪代码

图片 5

跻身调节和测试

准备工作成功之后就足以进来调节和测试了

cd 程序目录/bin/Debug/netcoreapp1.1/ubuntu.16.04-x64/publish
lldb-3.6 程序名称

图片 6

先是设置gc主函数的断点,然后运维程序

b gc1
r

图片 7

我们停在了gc一函数,今后能够用bt来看调用来源

图片 8

此次是手动触发GC,调用来源中含有了GCInterface::Collect和JIT生成的函数

亟需体现当前的本地变量能够用fr v,须求打字与印刷变量或然表达式能够用p

图片 9

现在用n来步过,用s来步进继续追踪代码

图片 10

跻身标志阶段

在上海教室的地方中用s一声令下就可以进入mark_phase,继续步过到下图的地方

图片 11

此刻先让大家看下堆中的对象,加载CoreCL本田CR-V提供的LLDB插件

plugin load libsosplugin.so

插件提供的吩咐能够查阅那里的文书档案

执行dumpheap查看堆中的状态

图片 12
图片 13

执行dso翻看堆和寄存器中援引的靶子

图片 14

执行dumpobj查看对象的新闻

图片 15

在那一轮gc中指标a b c都会存活下来,
也许您会对怎么b能存活下来感到讶异,对象b的引用分配在栈上,即时生命周期过了也不必然会失灵(rsp不会移回去)

br s -n Promote -c "(long)*ppObject == 0x00007fff5c01a2b8" # -n 名称 -c 条件
c # 继续执行

图片 16

接下去步进mark_object_simple函数,然后步进gc_mark1函数

图片 17

me re -s8 -c3 -fx o # 显示地址中的内存,8个字节一组,3组,hex格式,地址是o
p ((CObjectHeader*)o)->IsMarked() # 显示对象是否标记存活

咱俩得以理解的见到标志对象共处设置了MethodTable的指针|= 1

现在给PinObject下断点

br s -n PinObject -c "(long)*pObjRef == 0x00007fff5c01a1a0"
c

图片 18

能够见到只是调用Promote接下来传入GC_CALL_PINNED

一连步进到if (flags & GC_CALL_PINNED)下的pin_object

图片 19

能够见见pinned标记设置在同步索引块中

进去标识阶段

在上海体育场合的岗位中用s指令就可以进入mark_phase,继续步过到下图的岗位

图片 20

此时先让大家看下堆中的对象,加载CoreCLTucson提供的LLDB插件

plugin load libsosplugin.so

插件提供的授命可以查看那边的文书档案

执行dumpheap翻开堆中的状态

图片 21
图片 22

执行dso查阅堆和寄存器中引用的指标

图片 23

执行dumpobj翻开对象的音讯

图片 24

在那1轮gc中目的a b c都会存活下来,
可能你会对为啥b能存活下来认为愕然,对象b的引用分配在栈上,即时生命周期过了也不肯定会失效(rsp不会移回去)

br s -n Promote -c "(long)*ppObject == 0x00007fff5c01a2b8" # -n 名称 -c 条件
c # 继续执行

图片 25

接下去步进mark_object_simple函数,然后步进gc_mark1函数

图片 26

me re -s8 -c3 -fx o # 显示地址中的内存,8个字节一组,3组,hex格式,地址是o
p ((CObjectHeader*)o)->IsMarked() # 显示对象是否标记存活

我们能够清楚的观看标识对象共处设置了MethodTable的指针|= 1

现在给PinObject下断点

br s -n PinObject -c "(long)*pObjRef == 0x00007fff5c01a1a0"
c

图片 27

能够旁观只是调用Promote接下来传入GC_CALL_PINNED

连续步进到if (flags & GC_CALL_PINNED)下的pin_object

图片 28

能够看看pinned标识设置在同步索引块中

进入陈设阶段

跻身安插阶段后首先打字与印刷一下1一代的情事

p generation_table

选拔那几个命令能够看看gen 0 ~ gen 三的情景,最终多个成分是空成分不用在意

图片 29

继续步过下去到下图的那一段

图片 30

在此间大家找到了三个plug的起来,然后枚举已标识的对象,下图是擦除marked和pinned标识的代码

图片 31

在此间大家找到了四个plug的截至

图片 32

假使是Full GC恐怕不升代,在处理第三个plug在此以前就会安装gen 2的安顿代边界

图片 33

模仿压缩的地方

图片 34

假使x超过原来的gen 0的边际,设置gen 壹的布署代边界(原gen 一的目的变gen
二),
若是不升代那里也会设置gen 0的安插代边界

图片 35

依傍压缩后把原地点与削减到的地点的偏移值存到plug消息(plug前的壹块内部存款和储蓄器)中

图片 36

构建plug树

图片 37

设置brick表,这个plug树跨了6个brick

图片 38
图片 39

如若升代,模拟压缩全体完事后装置gen 0的安排代边界

图片 40

接下去倘诺不动里面的变量,将会进入清扫阶段(不满意进入削减阶段的基准)

进入计划阶段

跻身安排阶段后第2打字与印刷一下壹一代的状态

p generation_table

使用那么些命令能够看出gen 0 ~ gen 三的气象,最终一个成分是空成分不用在意

图片 41

持续步过下去到下图的那一段

图片 42

在此处大家找到了二个plug的始发,然后枚举已标志的对象,下图是擦除marked和pinned标识的代码

图片 43

在此间大家找到了贰个plug的告竣

图片 44

假若是Full GC或许不升代,在拍卖第3个plug从前就会设置gen 二的布置代边界

图片 45

宪章压缩的地方

图片 46

只要x超出原来的gen 0的界限,设置gen 一的安插代边界(原gen 1的目的变gen
二),
设若不升代这里也会安装gen 0的安顿代边界

图片 47

效仿压缩后把原地方与削减到的地址的偏移值存到plug音信(plug前的1块内部存款和储蓄器)中

图片 48

构建plug树

图片 49

设置brick表,这个plug树跨了6个brick

图片 50
图片 51

比方升代,模拟压缩全体完了后装置gen 0的布署代边界

图片 52

接下去倘若不动里面包车型大巴变量,将会进来清扫阶段(不满足进入削减阶段的原则)

进入清扫阶段

这一次为了调查对象c怎样被清扫,大家进入第3遍gc的make_free_lists

b make_free_lists
c

处理当下brick中的plug树

图片 53

前边看到的靶子c的地点是0x0000七fff伍c01a二e八,那里大家就看对象c前面的plug是怎么着处理的

br s -f gc.cpp -l 23070 -c "(long)tree > 0x00007fff5c01a2e8"
c

我们得以看看plug
0x00007fff伍c0一a300前边的悠闲空间中富含了对象c,空余空间的起来地址正是目的c

图片 54

接下去便是在那片空余空间中开创free object和加到free list了,
那里的高低不足(< min_free_list)所以只会成立free object不会加到free
list中

图片 55

设置代边界,以前陈设阶段模拟的布署代边界不会被选取

图片 56

清扫阶段完结后本次的gc的严重性工作就产生了,接下去让大家尊重稳固阶段和压缩阶段

进入清扫阶段

此番为了考查对象c如何被清扫,我们进入第3回gc的make_free_lists

b make_free_lists
c

拍卖当下brick中的plug树

图片 57

前方看到的靶子c的地方是0x00007fff5c0一a贰e八,那里大家就看对象c前面包车型地铁plug是哪些处理的

br s -f gc.cpp -l 23070 -c "(long)tree > 0x00007fff5c01a2e8"
c

我们能够看到plug
0x00007fff5c0一a300前边的空余空间中涵盖了目的c,空余空间的开始地址正是指标c

图片 58

接下去正是在这片空余空间中创设free object和加到free list了,
此处的轻重缓急不足(< min_free_list)所以只会创立free object不会加到free
list中

图片 59

设置代边界,以前布署阶段模拟的布置代边界不会被使用

图片 60

清扫阶段完毕后这一次的gc的最首要办事就实现了,接下去让咱们侧重稳固阶段和削减阶段

进去重平昔阶段

行使方面包车型大巴顺序让布置阶段选用压缩,要求修退换量,那里再一次运转程序并选用以下命令

b gc.cpp:22489
c
expr should_compact = true

图片 61

n步过到下图的岗位,s步进到relocate_phase函数

图片 62

到那一个职分能够看看用了和标识阶段一样的GcScanRoots函数,不过传入的不是Promote而是Relocate函数

图片 63

接下去下断点进入Relocate函数

b Relocate
c

GCHeap::Relocate函数不会重定位子对象,只是用来重平平昔源于根对象的引用

图片 64

一向走到那几个岗位然后进入gc_heap::relocate_address函数

图片 65

依照原地方和brick table找到呼应的plug树

图片 66

搜索plug树中old_address所属的plug

图片 67

基于plug中的reloc修改指针地址

图片 68

当今再来看relocate_surHTCrs函数,那几个函数用于重一贯存活下来的靶子中的引用

b relocate_survivors
c

图片 69

接下去会枚举并处理brick,走到此地进入relocate_survivors_in_brick函数,那个函数处理单个brick中的plug树

图片 70

递归处理plug树种的各种节点

图片 71

走到这里进入relocate_survivors_in_plug函数,这几个函数处理单个plug中的对象

图片 72

图中的这么些plug结尾被下二个plug覆盖过,须要新鲜处理,那里一而再进入relocate_shortened_survivor_helper函数

图片 73

当前是unpinned plug,下一个plug是pinned plug

图片 74

枚举处理plug中的各种对象

图片 75

万壹这一个目的结尾未被覆盖,则调用relocate_obj_helper重一贯目的中的各样成员

图片 76
图片 77

假诺目的结尾被遮住了,则调用relocate_shortened_obj_helper重一贯目的中的各种成员
在此间成员假若被遮住会调用reloc_ref_in_shortened_obj修改备份数据中的成员,但是因为go_through_object_nostart是1个macro那里不可能调节和测试内部的代码

图片 78

接下去大家着眼对象a的地点是还是不是改变了

再度运转并修改should_compact变量

b gc.cpp:22489
r
expr should_compact = true
plugin load libsosplugin.so
dso

咱俩能够看出指标a的地址在0x0000七fff伍c01a2b8,接下去给relocate_address函数下断点

图片 79

br s -n relocate_address -c "(long)(*pold_address) == 0x00007fff5c01a2b8"
c

图片 80

我们得以观望地点由0x00007fff5c0一a二b八化为了0x00007fff5c009一b捌

图片 81

接下去一向跳回plan_phase,下图能够见见重一直阶段达成现在新的地址上仍无对象,重一贯阶段只是修改了地址并未有复制内部存款和储蓄器,直到压缩阶段实现之后对象才会在新的地址

图片 82

接下去看压缩阶段

跻身重向来阶段

动用方面的程序让安顿阶段选取压缩,需求修改动量,那里再度运转程序并使用以下命令

b gc.cpp:22489
c
expr should_compact = true

图片 83

n步过到下图的岗位,s步进到relocate_phase函数

图片 84

到这么些职位能够看出用了和符号阶段同样的GcScanRoots函数,可是传入的不是Promote而是Relocate函数

图片 85

接下去下断点进入Relocate函数

b Relocate
c

GCHeap::Relocate函数不会重定位子对象,只是用来重一直来源于根对象的引用

图片 86

直接走到那几个岗位然后进入gc_heap::relocate_address函数

图片 87

依据原地方和brick table找到相应的plug树

图片 88

搜索plug树中old_address所属的plug

图片 89

传说plug中的reloc修改指针地址

图片 90

近来再来看relocate_surBlackBerryrs函数,这一个函数用于重一贯存活下来的目的中的引用

b relocate_survivors
c

图片 91

接下去会枚举并拍卖brick,走到此处进入relocate_survivors_in_brick函数,这些函数处理单个brick中的plug树

图片 92

递归处理plug树种的逐条节点

图片 93

走到那边进入relocate_survivors_in_plug函数,那个函数处理单个plug中的对象

图片 94

图中的这些plug结尾被下1个plug覆盖过,必要特殊处理,那里继续进入relocate_shortened_survivor_helper函数

图片 95

当前是unpinned plug,下一个plug是pinned plug

图片 96

枚举处理plug中的各样对象

图片 97

只要这些指标结尾未被遮住,则调用relocate_obj_helper重一向目的中的各种成员

图片 98
图片 99

假如目的结尾被遮住了,则调用relocate_shortened_obj_helper重一直目标中的种种成员
在那边成员借使被遮盖会调用reloc_ref_in_shortened_obj修改备份数据中的成员,然而因为go_through_object_nostart是一个macro那里不恐怕调节和测试内部的代码

图片 100

接下去我们着眼对象a的地址是还是不是改造了

再次运转并修改should_compact变量

b gc.cpp:22489
r
expr should_compact = true
plugin load libsosplugin.so
dso

我们能够见见指标a的地方在0x0000柒fff伍c0一a二b八,接下去给relocate_address函数下断点

图片 101

br s -n relocate_address -c "(long)(*pold_address) == 0x00007fff5c01a2b8"
c

图片 102

我们得以看来地点由0x0000柒fff5c0一a二b八变为了0x0000柒fff5c00玖一b八

图片 103

接下去一向跳回plan_phase,下图可以看来重一向阶段完毕以往新的地方上仍无对象,重从来阶段只是修改了地址并未有复制内部存款和储蓄器,直到压缩阶段实现之后对象才会在新的地点

图片 104

接下去看压缩阶段

进去削减阶段

在重一向阶段实现之后走到下图的职分,步进就能够进入削减阶段

图片 105

枚举brick table

图片 106

拍卖单个brick table中的plug树

图片 107

听别人讲下三个tree的gap总结last_plug的大小

图片 108

处理单个plug中的对象

图片 109

上面的last_plug是pinned plug所以不运动,这里找了其余多个会活动的plug

图片 110

下图能够见见整个plug都被复制到新的地方

图片 111

此地再找二个谈起底被覆盖过的plug看看是怎么处理的

图片 112

率先把被覆盖的结尾大小加回去

图片 113

接下来把被遮盖的始末近来恢复生机回去

图片 114
图片 115

复制完再把覆盖的始末调换回来,因为下二个plug还亟需用

图片 116

最终在recover_saved_pinned_info会全部重操旧业回去

进入削减阶段

在重一直阶段完结之后走到下图的任务,步进就能够进入削减阶段

图片 117

枚举brick table

图片 118

处理单个brick table中的plug树

图片 119

据说下三个tree的gap总括last_plug的大小

图片 120

处理单个plug中的对象

图片 121

上面的last_plug是pinned plug所以不挪窝,那里找了其它一个会活动的plug

图片 122

下图能够看到全部plug都被复制到新的地址

图片 123

此处再找一个聊到底被覆盖过的plug看看是怎么处理的

图片 124

率先把被掩盖的末梢大小加回去

图片 125

然后把被遮住的内容目前复苏回去

图片 126
图片 127

复制完再把覆盖的始末交流回来,因为下3个plug还必要用

图片 128

最终在recover_saved_pinned_info会全部复苏回去

参考链接

https://github.com/dotnet/coreclr/blob/master/Documentation/botr/garbage-collection.md
https://github.com/dotnet/coreclr/blob/release/1.1.0/Documentation/building/linux-instructions.md
https://github.com/dotnet/coreclr/blob/release/1.1.0/Documentation/building/debugging-instructions.md
http://lldb.llvm.org/tutorial.html
http://lldb.llvm.org/lldb-gdb.html

参照链接

https://github.com/dotnet/coreclr/blob/master/Documentation/botr/garbage-collection.md
https://github.com/dotnet/coreclr/blob/release/1.1.0/Documentation/building/linux-instructions.md
https://github.com/dotnet/coreclr/blob/release/1.1.0/Documentation/building/debugging-instructions.md
http://lldb.llvm.org/tutorial.html
http://lldb.llvm.org/lldb-gdb.html

写在末了

那壹篇中自笔者列出了多少个gc中相比根本的有个别,不过还有成千上百处可以追究的有的,
万1您有乐趣能够友善试着用lldb调节和测试CoreCL奥迪Q5,能够学到繁多文档和图书之外的文化,
专门是对于CoreCL宝马X三那种文档少注释也少的花色,通晓调节和测试工具得以小幅度减小精通代码所需的光阴

写完那一篇作者将暂停探究GC,下1篇开首会介绍JIT相关的剧情,敬请期待

写在最后

那1篇中自己列出了多少个gc中相比较根本的1对,不过还有成千上百处能够追究的壹些,
只要你有意思味能够团结试着用lldb调节和测试CoreCLLAND,可以学到多数文书档案和本本之外的知识,
专门是对此CoreCLPAJERO这种文书档案少注释也少的系列,通晓调节和测试工具得以大幅度压缩精通代码所需的时日

写完那一篇笔者将中断研商GC,下1篇先河会介绍JIT相关的剧情,敬请期待