调试实战 | 记一次有教益的 vs2022 内存分配失败崩溃分析(续)

前言

前一阵子遇到了 vs2022 卡死的问题,在上一篇文章中重点分析了崩溃的原因 —— 当 vs2022 尝试分配 923MB 的内存时,物理内存+页文件大小不足以满足这次分配请求,于是抛出异常。

本篇文章将重点挖掘一下 vs2022 在崩溃之前已经分配的内容。

说明: 本文很早就写了草稿,一直没时间整理发布,Finally~

还是先从调用栈入手,找到关键参数,然后查看参数内容。

查找 vector 对象地址

栈帧 0b 对应的函数是 std::vector<T>::_Emplace_reallocate(),栈帧 0c 会调用这个函数。根据调用约定可知,调用类成员函数时,rcx 会指向类对象,在这里 rcx 会指向 std::vector<std::shared_ptr<std::stringstream>> 类型的实例。可以通过查看栈帧 0c 的反汇编代码确定 rcx 的来源。

211ae8ef5635dad978a289635c862e71.png
view-vector-instance-from-stackframe-0c

从图中可知,rcx 的值来自 rbp-0x70。那 rbp 的值是多少呢?使用 uf 查看 vcpkg!code_store::a_store::a_thread_impl::append_code_item_name() 函数的反汇编代码。

f84fa994be5908b6e080f577eae7938a.png
view-rbp-in-frame-0c

由上图可知,先把 rsp-0x920 赋值给  rbp,然后 rsp 会减小 0xa20。所以可以通过 rsp+0xa20-0x920 计算出对应的 rbp 的值,再减去 0x70 即可得到 rcx 的值。由此可知 vector 对象的地址是 0x000000b1 6547e5d0

6320c20bb9595394374244f7b4f982a7.png
view-rbp-rcx-in-frame-0c

查看 vector 内容

查阅 vs 提供的 STL 源码可知,vector 对象起始偏移 0 的位置存储了第一个元素的地址,起始偏移 864位程序)的位置存储了最后一个元素后面的地址。可以查看 vector 中前 20 个元素。

c8cb3c0230548b6573724f487ff39b28.png
view-vector-front-20-data

由调用栈可知,vector 中的元素类型是 shared_ptr<stringstream>。根据源码可知,shared_ptr<T> 类型的大小是 16 字节,偏移 0 的位置存储了对象的地址,偏移 8 的位置存储了引用计数对象的地址。

template <class _Ty>
class shared_ptr : public _Ptr_base<_Ty> { // class for reference counted resource management...
};template <class _Ty>
class _Ptr_base { // base class for shared_ptr and weak_ptr
private:element_type* _Ptr;_Ref_count_base* _Rep;...
};

vector 中有多少个元素

大家应该都知道,vector 中的元素是顺序存储的,知道了起始地址及结束地址,也知道每个元素的大小,可以很容易计算出 vector 中的元素数量。

windbg 中输入 ? (000001c2434b7170-000001c21ccdd060) / 0n16 可以得到元素个数 40360465

根据上次分析的结果可知,分配的元素数量是60540697。通过查看 vs 提供的源码可知,容器扩容时会按 1.5 倍进行扩容。

来验证以一下是否符合这个规律。在 windbg 中输入 ? 0n40360465 + 0n40360465 / 2 可以得到结果 60540697

70bcc7d1e1c5eb945cdca443b3cb0722.png
view-vector-size

可见,当时 vs 在调用类似 push_back() 之类的方法向容器中增加元素,但是容器正好满了,触发了扩容操作。由此也可以验证之前的分析是正确的。

验证引用计数对象数据

拿第一个元素进行验证,实际对象的地址是 000001be 580056f0,引用计数对象的地址是 000001be 580056e0。先验证引用计数对象是否正确。

_Ref_count_base 结构如下图所示:

149ecf39ee99f6bd1cc0b2fb233ccddb.png
view-_Ref_count_base_detail

说明: devenv 加载的模块所对应的调试符号已经去除了 Type 信息,没办法通过 dt 显示类型信息。上图是我用 windbg 调试新建的测试工程时的截图。

从下图可知,引用计数相关数据是完美匹配的。

367f1d298f7d4e5907f7ed499b217039.png
verify-reference-count-object

一般 shared_ptr<T> 的引用计数和实际的数据是没有关系的,比如下面的代码:

int* p = new int();
std::shared_ptr<int> sp(p);
62dcce10ce63a1f18d63a2773480e902.png
view_normal_shared_ptr

sp._Ptr 的值是 0x017b9450sp._Rep 的地址是 0x017b9640,两者之间没有明显关系。

但是,如果你观察的比较仔细,可以发现一个非常有趣的现象 ——  vector 中的每个元素(智能指针)的引用计数对象的地址 +0x10 正好等于实际对象的地址。

以第一个元素为例,引用计数对象的地址是 000001be 580056e0,实际对象的地址是 000001be 580056f0,两者正好相差了 0x10

这是怎么回事呢?如果你对 stl 比较熟悉,可能已经想到了 std::make_shared()vector 中存储的对象都是通过 std::make_shared() 创建出来的。

make_shared

我摘录了 vs 中提供的源码

template <class _Ty, class... _Types>
shared_ptr<_Ty> make_shared(_Types&&... _Args) { // make a shared_ptr to non-array objectconst auto _Rx = new _Ref_count_obj2<_Ty>(_STD forward<_Types>(_Args)...);shared_ptr<_Ty> _Ret;_Ret._Set_ptr_rep_and_enable_shared(_STD addressof(_Rx->_Storage._Value), _Rx);return _Ret;
}

注意代码中 _Ret._Set_ptr_rep_and_enable_shared() 第一个参数的值是 _Rx->_Storage._Value 的地址。

_Rx 的类型是 _Ref_count_obj2<_Ty>*_Ref_count_obj2 继承自 _Ref_count_base。而  _Ref_count_base 的大小是 16 字节:虚表指针 8 字节,两个引用计数各占 4 字节,一共 16 字节。

大概的内存结构图如下:

a5867cd83e2d9366cc86c925192c6144.png
make_shared_relation

务必注意 _Ref_count_obj2 中的 _Storage 存储了整个目标对象,而不是指针。

总结

  • procdump 真是事后调试的好帮手。以管理员权限运行 procdump -i -ma d:\dumps\ 即可安装。-i 表示安装(如果要卸载,可以使用 -u 参数)。-ma 表示执行完整转储,d:\dumps\ 表示 .dmp 文件保存的位置。

  • 相较于 32 位进程的 4GB232 次方)虚拟内存空间而言, 64 位进程的虚拟内存空间超级大,目前是  256TB(总共 64 位,目前只用了 48 位),内核态和用户态平均分,用户态可以使用一半,也就是 128TB

  • 如果使用 malloc() 或者 new() (内部会调用 malloc())分配的内存大小超出堆阈值,那么内部会使用 NtAllocateVirtualMemory() 分配内存,而且 AllocationType 的值是 MEM_COMMIT。分配 MEM_COMMIT 类型的内存是受物理内存+分页文件大小限制的。

参考资料

  • vs 源码

  • NTSTATUS Values

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/32017.shtml

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

相关文章

HTML静态网页成品作业(HTML+CSS+JS)——动漫斗罗大陆介绍网页(3个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;使用Javacsript代码实现图片轮播和tab切换&#xff0c;共有3个页面。 …

24年安克创新社招入职自适应能力cata测评真题分享北森测评高频题库

第一部分&#xff1a;安克创新自适应能力cata测评 感谢您关注安克创新社会招聘&#xff0c;期待与您一起弘扬中国智造之美。 为对您做出全面的评估&#xff0c;现诚邀您参加我们的在线测评。 测评名称&#xff1a;社招-安克创新自适应能力cata测评 第二部分&#xff1a;安克…

福建聚鼎:装饰画现在做起来难吗

在当代社会&#xff0c;艺术创作已经成为很多人表达自我、追求美学生活的方式之一。装饰画作为家居装饰的重要元素&#xff0c;也受到了越来越多人的喜爱。但做一个优质的装饰画真的容易吗? 从技术层面讲&#xff0c;随着科技的发展&#xff0c;制作装饰画的手段和材料都比以往…

【因果推断python】50_去偏/正交机器学习2

目录 Frisch-Waugh-Lovell on Steroids CATE Estimation with Double-ML Frisch-Waugh-Lovell on Steroids 双重/偏差 ML 其思想非常简单&#xff1a;在构建结果和治疗残差时使用 ML 模型&#xff1a; 是估计&#xff0c;是估计 我们的想法是&#xff0c;ML 模型具有超强的…

Autodesk Revit产品痛点分析

1.Revit已有20多年的历史&#xff0c;大多数软件公司认为大多数代码最多只有10年的生命周期。 2.Revit核心部分仍局限于单个CPU核心上,严重制约性能提升。 3.Revit只在数据库的大小和小细节上的改动。 4.Revit陈旧的绘图技术和性能难以提升。 5.Revit的致命弱点是模型增长的…

Red Hat Ansible Automation Platform架构

目录 示例架构&#xff1a;一、Ansible Automation Platform 实现流程详解1. 自动化控制器 (Automation Controller)2. 自动化网格 (Automation Mesh)3. 私有自动化中心 (Private Automation Hub)4. Event-Driven Ansible 控制器5. 数据存储 (PostgreSQL 数据库) 二、实现流程1…

C/C++打假:条件分支语句switch..case效率比if..else高?

很久很久以前&#xff0c;有人教导我说条件分支大于4条时&#xff0c;switch..case效率会比if..else高&#xff0c;条件分支为10条时&#xff0c;switch..case效率会比if..else快一倍不止。随着条件分支越多&#xff0c;效率差异越大。今日得闲&#xff0c;我做了个测试来验证这…

pyqt5 信号和槽函数以及Qthread 多线程的简单的例子

写了一个简单的例子&#xff1a; 包含一个主窗口和一个按钮。点击按钮时&#xff0c;我们将启动一个耗时的任务&#xff08;在这里我们使用time.sleep来模拟&#xff09;。为了不阻塞主线程&#xff0c;我们将在一个单独的线程中运行这个任务。同时&#xff0c;我们将显示一个进…

论文解读:Autoregressive Image Generation without Vector Quantization

这篇论文的主要内容围绕着一个核心问题&#xff1a;是否有必要将自回归模型与向量量化的表示方式绑定在一起&#xff0c;特别是在图像生成领域&#xff1f;作者团队来自麻省理工学院计算机科学与人工智能实验室&#xff08;MIT CSAIL&#xff09;、谷歌DeepMind以及清华大学&am…

力扣SQL 即时食物配送 II min函数 嵌套查询

Problem: 1174. 即时食物配送 II &#x1f468;‍&#x1f3eb; 参考题解 Code -- 计算立即配送的订单百分比 select round (-- 计算订单日期与客户偏好配送日期相同的订单数量sum(case when order_date customer_pref_delivery_date then 1 else 0 end) * 100 /-- 计算总订…

基于深度学习的图像识别技术与应用是如何?

基于深度学习的图像识别技术与应用在当今社会中扮演着越来越重要的角色。以下是对该技术与应用的详细解析&#xff1a; 一、技术原理 深度学习是一种模拟人脑处理和解析数据的方式的技术和方法论。在图像识别领域&#xff0c;深度学习主要通过深度神经网络&#xff08;如卷积…

CentOS7在2024.6.30停止维护后,可替代的Linux操作系统

背景 Linux的发行版本可以大体分为两类&#xff0c;一类是商业公司维护的发行版本&#xff0c;一类是社区组织维护的发行版本&#xff0c;前者以著名的Redhat&#xff08;RHEL&#xff09;为代表&#xff0c;后者以Debian为代表。国内占有率最多的却是Centos&#xff0c;这是由…

最全信息收集工具集

吉祥学安全知识星球&#x1f517;除了包含技术干货&#xff1a;Java代码审计、web安全、应急响应等&#xff0c;还包含了安全中常见的售前护网案例、售前方案、ppt等&#xff0c;同时也有面向学生的网络安全面试、护网面试等。 所有的攻防、渗透第一步肯定是信息收集了&#xf…

CID引流电商助力3C产品销售腾飞的实践与思考

摘要&#xff1a;随着互联网技术的不断发展和普及&#xff0c;电商行业迎来了前所未有的发展机遇。其中&#xff0c;CID引流电商作为一种新兴的电商模式&#xff0c;为商家们提供了更加精准、高效的拓客之路。尤其在3C产品领域&#xff0c;CID引流电商更是助力其销售腾飞的重要…

Python 学习 第四册 第10章 系统(2)

-----用教授的方式学习 目录 10.3 进程 10.3.1 使用subprocess创建进程 10.3.2 使用multiprocessing创建进程 10.3.3 使用terminate()终止进程 10.4 日期和时间 10.4.1 datetime模块 10.4.2 使用time模块 10.4.3 读写日期和时间 10.3 进程 当运行一个程序时,操…

云计算【第一阶段(18)】磁盘管理与文件系统

一、磁盘基础 磁盘&#xff08;disk&#xff09;是指利用磁记录技术存储数据的存储器。 磁盘是计算机主要的存储介质&#xff0c;可以存储大量的二进制数据&#xff0c;并且断电后也能保持数据不丢失。 早期计算机使用的磁盘是软磁盘&#xff08;Floppy Disk&#xff0c;简称…

程序猿大战Python——面向对象——魔法方法

什么是魔法方法&#xff1f; 目标&#xff1a;了解什么是魔法方法&#xff1f; 魔法方法指的是&#xff1a;可以给Python类增加魔力的特殊方法。有两个特点&#xff1a; &#xff08;1&#xff09;总是被双下划线所包围&#xff1b; &#xff08;2&#xff09;在特殊时刻会被…

调查问卷管理系统设计文档

一、项目背景和目标 随着现代企业对市场研究的深入&#xff0c;调查问卷已成为获取用户反馈和市场动态的重要工具。为了高效管理问卷的创建、发布、回收和分析&#xff0c;我们设计了一套调查问卷管理系统。本系统的目标是提供一个功能完善、操作简便、性能稳定的平台&#xff…

MURF3040CTR-ASEMI智能AI应用MURF3040CTR

编辑&#xff1a;ll MURF3040CTR-ASEMI智能AI应用MURF3040CTR 型号&#xff1a;MURF3040CTR 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 恢复时间&#xff1a;35ns 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;30A 最大循环峰值反向电压&#xff08;VR…

CSS详解

盒子模型&#xff08;box-sizing&#xff09; line-height与height CSS选择符和可继承属性 属性选择符&#xff1a; 示例&#xff1a;a[target"_blank"] { text-decoration: none; }&#xff08;选择所有target"_blank"的<a>元素&#xff09; /* 选…