关于如何行使LLDB调节和测试CoreCL君越的介绍能够看,在上一篇中作者分析了CoreCL奔驰M级中GC的中间处理

在上一篇中作者分析了CoreCLLX570中GC的中间处理,
在这一篇我将选取LLDB实际跟踪CoreCL福睿斯中GC,关于怎样采纳LLDB调节和测试CoreCL库罗德的牵线能够看:

在上一篇中自个儿分析了CoreCLLacrosse中GC的内部处理,
在这一篇小编将动用LLDB实际跟踪CoreCL君越中GC,关于如何行使LLDB调试CoreCL途乐的牵线能够看:

  • 微软官方的文书档案,地址
  • 自作者在第三篇中的介绍,地址
  • 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");
        }
    }
}

防微杜渐调试

环境和本身的第一篇作品一样,都以ubuntu 16.04 LTS,首先必要透露程序:

dotnet publish

通知程序后,把温馨编写翻译的coreclr文件覆盖到宣布目录中:
复制coreclr/bin/Product/Linux.x64.Debug下的文本到程序目录/bin/Debug/netcoreapp1.1/ubuntu.16.04-x64/publish下。
请不要设置开启服务器GC,一来是那篇小说分析的是工作站GC的处理,二来开启服务器GC很简单导致调节和测试时死锁。

准备调节和测试

条件和作者的第一篇小说一样,都以ubuntu 16.04 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

率先设置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

我们停在了gc1函数,以后得以用bt来看调用来源

图片 8

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

内需体现当前的本地变量可以用fr v,必要打印变量大概表明式能够用p

图片 9

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

图片 10

进入标记阶段

在上图的职位中用s指令即可进入mark_phase,继续步过到下图的职位

图片 11

那时候先让我们看下堆中的对象,加载CoreCL奥德赛提供的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

此时先让我们看下堆中的对象,加载CoreCLHaval提供的LLDB插件

plugin load libsosplugin.so

插件提供的命令能够查看这边的文书档案

执行dumpheap翻开堆中的状态

图片 21
图片 22

执行dso查阅堆和寄存器中援引的靶子

图片 23

执行dumpobj翻开对象的新闻

图片 24

在这一轮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标记设置在同步索引块中

进去安插阶段

进入陈设阶段后率先打字与印刷一下顺序代的气象

p generation_table

选拔那几个命令可以观望gen 0 ~ gen 3的情事,最终一个要素是空元素不用在意

图片 29

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

图片 30

在此间大家找到了一个plug的始发,然后枚举已标记的靶子,下图是擦除marked和pinned标记的代码

图片 31

在此间大家找到了1个plug的收尾

图片 32

倘就算Full GC也许不升代,在处理第2个plug在此以前就会设置gen 2的陈设代边界

图片 33

模仿压缩的地方

图片 34

比方x越过原来的gen 0的界限,设置gen 1的安顿代边界(原gen 1的目的变gen
2),
若果不升代那里也会安装gen 0的陈设代边界

图片 35

依样葫芦压缩后把原地方与削减到的地方的偏移值存到plug音讯(plug前的一块内部存款和储蓄器)中

图片 36

构建plug树

图片 37

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

图片 38
图片 39

假定升代,模拟压缩全体成功后安装gen 0的陈设代边界

图片 40

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

进入安排阶段

跻身安插阶段后第①打字与印刷一下挨家挨户代的境况

p generation_table

应用那个命令能够见到gen 0 ~ gen 3的动静,最终1个因素是空成分不用在意

图片 41

接轨步过下去到下图的这一段

图片 42

在此处我们找到了二个plug的始发,然后枚举已标记的指标,下图是擦除marked和pinned标记的代码

图片 43

在此间我们找到了二个plug的收尾

图片 44

设假诺Full GC或许不升代,在处理第三个plug在此之前就会设置gen 2的安顿代边界

图片 45

仿照压缩的地方

图片 46

假若x越过原来的gen 0的疆界,设置gen 1的陈设代边界(原gen 1的对象变gen
2),
一旦不升代那里也会设置gen 0的安顿代边界

图片 47

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

图片 48

构建plug树

图片 49

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

图片 50
图片 51

如若升代,模拟压缩全体做到后装置gen 0的安顿代边界

图片 52

接下去假使不动里面包车型地铁变量,将会进来清扫阶段(不知足进入削减阶段的标准化)

进入清扫阶段

本次为了考察对象c如何被清扫,大家进入第一遍gc的make_free_lists

b make_free_lists
c

处理当下brick中的plug树

图片 53

前边看到的靶子c的地方是0x00007fff5c01a2e8,这里大家就看对象c后边的plug是哪些处理的

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

大家能够见到plug
0x00007fff5c01a300前方的闲暇空间中蕴藏了指标c,空余空间的启幕地址正是目的c

图片 54

接下去正是在那片空余空间中创设free object和加到free list了,
此处的深浅不足(< min_free_list)所以只会创建free object不会加到free
list中

图片 55

设置代边界,从前布置阶段模拟的布置代边界不会被运用

图片 56

清扫阶段实现后本次的gc的根本工作就到位了,接下去让我们讲究稳定阶段和压缩阶段

进入清扫阶段

本次为了考察对象c怎么着被清扫,大家进来第三次gc的make_free_lists

b make_free_lists
c

拍卖当下brick中的plug树

图片 57

前方看到的目的c的地址是0x00007fff5c01a2e8,这里大家就看对象c前边的plug是什么样处理的

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

咱俩得以看到plug
0x00007fff5c01a300前边的空闲空间中包括了对象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_surBlackBerryrs函数,那几个函数用于重从来存活下来的靶子中的引用

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是三个macro那里无法调节和测试内部的代码

图片 78

接下去大家阅览对象a的地点是不是变动了

双重运转并修改should_compact变量

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

大家得以看来目的a的地址在0x00007fff5c01a2b8,接下去给relocate_address函数下断点

图片 79

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

图片 80

我们能够看出地方由0x00007fff5c01a2b8成为了0x00007fff5c0091b8

图片 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_surOPPOrs函数,这几个函数用于重一直存活下来的目的中的引用

b relocate_survivors
c

图片 91

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

图片 92

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

图片 93

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

图片 94

图中的这么些plug结尾被下二个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是2个macro那里不恐怕调节和测试内部的代码

图片 100

接下去大家着眼对象a的位置是或不是变动了

再也运转并修改should_compact变量

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

我们得以看到目的a的地点在0x00007fff5c01a2b8,接下去给relocate_address函数下断点

图片 101

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

图片 102

小编们能够见到地点由0x00007fff5c01a2b8变成了0x00007fff5c0091b8

图片 103

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

图片 104

接下去看压缩阶段

进入削减阶段

在重一贯阶段实现将来走到下图的职位,步进即可进入削减阶段

图片 105

枚举brick table

图片 106

拍卖单个brick table中的plug树

图片 107

依据下3个tree的gap总结last_plug的大小

图片 108

拍卖单个plug中的对象

图片 109

上面的last_plug是pinned plug所以不运动,那里找了其它1个会活动的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

复制完再把覆盖的内容交流回来,因为下2个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中相比较首要的片段,不过还有成千上百处能够探索的一部分,
设若你有趣味能够友善试着用lldb调节和测试CoreCL纳瓦拉,能够学到很多文书档案和图书之外的学识,
尤其是对此CoreCL途胜这种文书档案少注释也少的类别,精通调节和测试工具得以大幅度缩减驾驭代码所需的时间

写完这一篇作者将暂停琢磨GC,下一篇开端会介绍JIT相关的内容,敬请期待

写在最终

这一篇中本身列出了多少个gc中比较重庆大学的有个别,但是还有成千上百处可以探索的有的,
假定你有趣味能够团结试着用lldb调节和测试CoreCL本田UR-V,能够学到很多文书档案和书籍之外的学问,
专门是对此CoreCL中华V那种文书档案少注释也少的档次,驾驭调节和测试工具得以大幅度削减明白代码所需的时光

写完这一篇小编将中断研商GC,下一篇开端会介绍JIT相关的内容,敬请期待

相关文章