TCMalloc解密

Sticky post

写在前面

本文首先简单介绍TCMalloc及其使用方法,然后解释TCMalloc替代系统的内存分配函数的原理,再从宏观上讨论其内存分配的策略,在此之后再深入讨论实现细节。

有几点需要说明:

  • 本文只讨论gperftools中TCMalloc部分的代码,对应版本gperftools-2.7
  • 本文是根据TCMalloc源码以及简短的官方介绍作出的个人理解,难免有纰漏之处,且难以覆盖TCMalloc的方方面面,不足之处还请各位看官留言指正。
  • 除非特别说明,以下讨论均以32位系统、TCMalloc默认page大小(8KB)为基础,不同架构或不同page大小,有些相关数值可能不一样,但基本原理是相似的。
  • 为了控制篇幅,我会尽量少贴大段代码,只给出关键代码的位置或函数名,各位看官可自行参阅TCMalloc相关代码。

深究一次常见的CoreDump问题

碰到一个dangling pointer导致进程core的问题,此类问题很常见,从上图m_vRelativePosition.yz的异常值可以猜测,m_targetDomArea指向的内存被踩坏了,或是m_targetDomArea指向的对象已被销毁,其内存可能已被重新分配。

这里不去讨论m_targetDomArea野的原因(熟悉这块逻辑的同事已经在上图指出具体原因),只去研究导致core的直接原因是什么。

GDB调试时遇到的寄存器识别问题——Unexpected size of section `.reg-xstate’

近期用gdb调试coredump文件时发现会有warning:

warning: Unexpected size of section `.reg-xstate/13997' in core file.

起初以为是core文件有问题,后发现其实是低版本gdb(8.0版本)无法识别CPU的某些寄存器而做出的提示。

如果CPU支持Intel MPX技术,则会有几个额外的寄存器,而低版本的gdb无法识别这些寄存器,因此提示warning。

可以通过以下命令确定CPU是否支持MPX:

$ grep mpx /proc/cpuinfo 
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single fsgsbase bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 arat avx512_vnni

更换高版本(10.0版本)的gdb后,warning消失,且通过gdb命令info all-registers也确实可以查看到MPX相关的几个寄存器:

(gdb) info all-registers
bndcfgu        {raw = 0x0, config = {base = 0x0, reserved = 0x0, preserved = 0x0, enabled = 0x0}} {raw = 0x0, config = {base = 0, reserved = 0, preserved = 0, enabl--Type <RET> for more, q to quit, c to continue without paging--
ed = 0}}
bndstatus      {raw = 0x0, status = {bde = 0x0, error = 0x0}} {raw = 0x0, status = {bde = 0, error = 0}}
bnd0           {lbound = 0x0, ubound = 0xffffffffffffffff} : size -1 {lbound = 0x0, ubound = 0xffffffffffffffff} : size -1
bnd1           {lbound = 0x0, ubound = 0xffffffffffffffff} : size -1 {lbound = 0x0, ubound = 0xffffffffffffffff} : size -1
bnd2           {lbound = 0x0, ubound = 0xffffffffffffffff} : size -1 {lbound = 0x0, ubound = 0xffffffffffffffff} : size -1
bnd3           {lbound = 0x0, ubound = 0xffffffffffffffff} : size -1 {lbound = 0x0, ubound = 0xffffffffffffffff} : size -1

利用ChatGPT搞懂std::string=NULL引发的coredump

You

std::string str = NULL; 为什么会导致coredump

在C++中,std::string是一个类,用于表示字符串。当您试图将NULL分配给std::string对象时,会引发未定义行为,因为std::string不应该接受NULL作为其值。在这种情况下,未定义行为可能导致coredump,因为您试图将一个空指针赋值给std::string对象。

如果您想要创建一个空的std::string,您可以简单地使用如下方法:

std::string str; // 创建一个空的 std::string

或者,如果您想要创建一个包含特定内容的std::string,您可以这样做:

std::string str("some content");

总之,避免将NULL分配给std::string对象,以避免未定义行为和潜在的coredump。

You

为什么编译可以通过

我之前的回答可能有误,导致您的困惑。实际上,将NULL分配给std::string在某些编译器和情况下可能会通过编译。原因是NULL在C++中通常定义为整数零,而不是一个真正的空指针。因此,将NULL分配给std::string时,编译器可能会尝试将整数零转换为std::string

然而,即使编译通过,这种用法仍然是不正确的,并可能导致运行时错误,如coredump。这是因为std::string并不期望接收整数值作为其构造函数的参数,尤其是一个零值。

为了避免这种问题,您应该遵循正确的初始化方法,如我之前所示。在C++11及更高版本中,您还可以使用nullptr来表示真正的空指针,而不是使用NULL。这将有助于避免类型混淆和相关的问题。

总之,虽然在某些情况下,将NULL分配给std::string可能会通过编译,但这仍然是错误的用法,可能导致运行时错误。您应该遵循正确的std::string初始化方法,以避免此类问题。

【译】NMGen – 细节网格生成

本页介绍了构建导航网格的第五个也是最后一个阶段,即生成具有详细高度信息的三角形网格。

源数据类:PolyMeshFieldOpenHeightField
构建类:DetailMeshBuilder
数据类:TriangleMesh

如果你需要了解此阶段执行的操作,请参见处理过程概览

原文
This page describes the fifth and final stage in building a navigation mesh, the generation of a triangle mesh with detailed height information.

Source data class: PolyMeshField and OpenHeightField
Builder class: DetailMeshBuilder
Data class: TriangleMesh

If you need an overview of what is performed in this stage, please refer back to the high level process.

为什么要执行这一步

如果将原始源网格的可通过表面投影到xz平面上,并与上一阶段生成的多边形网格重叠,两者将会非常匹配。但在三维空间中,多边形网格可能无法充分遵循源网格的高度轮廓 。这个阶段增加了高度细节,这样细节网格(detail mesh)将在所有轴上与源网格的表面相匹配。为了实现这一点,我们遍历所有多边形并在多边形与源网格过度偏离时沿着多边形的边和其表面插入顶点。

在下面的例子中,楼梯附近的多边形网格在xz平面上匹配,但在y轴上偏差很大。

【译】NMGen - 凸多边形生成

本页描述了构建导航网格的第四个阶段,即从由轮廓表示的简单多边形生成凸多边形。这也是我们从体素空间回到向量空间的地方。

源数据类:ContourSet
构建类:PolyMeshFieldBuilder
数据类:PolyMeshField

如果您需要了解此阶段执行的操作,请参考处理过程概览

概述

本阶段的主要任务如下:

  • 轮廓的顶点在体素空间中,这意味着它们的坐标采用整数格式并表示到高度场原点的距离。因此轮廓顶点数据被转换为原始源几何体的向量空间坐标。
  • 每个轮廓完全独立于所有其他轮廓。在此阶段,我们合并重复的数据并将所有内容合并到一个单一网格中。
  • 轮廓只能保证表示简单的多边形,包括凸多边形和凹多边形。凹多边形对导航网格来说是没有用的,所以我们根据需要细分轮廓以获得凸多边形。
  • 我们收集指示每个多边形的哪些边连接到另一个多边形的连接信息(多边形邻居信息)。

坐标转换和顶点数据合并是相对简单的过程,所以我不会在这里讨论它们。如果您对这些算法感兴趣,可以查看文档详尽的源代码。这里我们将专注于凸多边形的细分。对每个轮廓会执行以下步骤:

  1. 对每个轮廓进行三角面化(triangulate)。
  2. 合并三角形以形成最大可能的凸多边形。

我们通过生成邻居连接信息来结束这个阶段。

Page 1 of 7

Powered by WordPress & Theme by Anders Norén

粤ICP备2021073162号-1