前言
前一阵子,使用小乌龟(TortoiseGit
)提交代码的时候,错误的 Revert
了部分代码,本文记录了找回这部分代码的过程。文章标题致敬张银奎老师《格蠹汇编》的第一章 —— 从堆里抢救丢失的博客。
说明: 本文的截图都是我用新建的示例工程截取的。
缘起
最近,程序运行的时候,执行某个功能会崩溃,根据经验猜测,应该是序列化,反序列化的问题。由于手里没有关键的 pdb
,调试起来比较费劲,而且项目比较急,暂时先不使用这个功能。准备先提交其它功能的代码。
大意失荆州
按照惯例,提交之前先检查一下提交内容。(p.s. 这是个好习惯)
发现有一部分代码会导致序列化有问题。一激动(当时被那个崩溃问题搞得很烦躁),点击了Revert...
。
点完了就后悔了 —— 这段代码是为其它功能写的,应该保留。由于在提交代码前,关闭了vs
,没办法通过 vs
找回了。
说明: 如果文件在
vs
外部被修改,vs
会给出类似下图的提示,这时候我们选No
不重新加载就可以了。
这可是我辛辛苦苦,一行一行敲出来的啊。就这么 “丢了” 吗?丢是不可能丢的,这辈子都不可能丢的。
峰回路转
幸亏没有烦躁到直接干掉小乌龟。想起张老师的《格蠹汇编》第一章就是 “从堆里抢救丢失的博客”,讲的是使用 windbg
从浏览器中找回未能成功发表的博文的故事。赶紧使用 windbg
附加到小乌龟上。先用 .dump /ma e:\dumps\tortoisegit.dmp
保存一份完整转储。
有了转储文件,即使关闭小乌龟也不怕了!稍微平复下我跌宕起伏的内心,应该用哪个命令搜索内存呢?很早之前从张老师的文章里了解到 s
命令可以搜索内存。加之,前一段日志刚好尝试解决过类似的问题,做了笔记。很快就把上次整理好的命令粘贴到 windbg
中进行查找。
搜寻关键字
有印象的关键字是 args.Contains("--all")
。在 windbg
中输入 !address -f:heap,PAGE_READWRITE -c:"s -u %1 %2 args.Contains(\"--all\")"
搜到两处,分别使用 du
命令查看这两处的内容。
明显第一处的内容比较全,选用第一处的地址进行进一步的搜索。如果能找到文件的开始和结束就最好了。经过简单的尝试,找到了开始和结束的地址。截图的话会比较长,这里就不截图了。开始地址是 0000022f``dae8e5f8-0x3538
,结束地址是 0000022f``dae8f030
。
说明: 也可以直接使用命令 s -u 0x0 L?0xffffffff`ffffffff "args.Contains("--all")",更简单明了。
保存到文件
知道开始地址和结束地址了,剩下的就是如何把对应的内容保存到文件中了。windbg
已经为我们准备好了一条命令 —— .writemem
。输入:.writemem e:\dumps\tortoisegit.cs 0000022f``dae8e5f8-0x3538 0000022f``dae8f030
即可把指定范围的数据保存到文件中。
查看保存的文件,果然是对应的文件内容!比较长,这里就不放截图了。
反思
一定要养成一个良好的版本管理习惯。开发新功能的时候,最好建立一个新分支,并且随时把变更提交。等开发完了,再合并回主分支,并删掉功能分支。如果我遵循了这个原则的话,就不会出现这种问题了。当然,也不会有本篇总结了。
遇到问题,一定不要鲁莽,保持冷静。如果我关了小乌龟,那么丢失的代码就真的没办法找回来了。
一定要养成总结问题,记录问题的好习惯!之前,有同事也遇到了类似的问题,代码不小心弄丢了。不幸的是,没能通过这种办法找回来。幸运的是,当时调查的结果都有记录,所以这次查这个问题的时候,翻出笔记。复制粘贴,回车,搞定!一气呵成,相当舒爽!
总结
使用
!address -f:heap, PAGE_READWRITE -c:"s -u %1 %2 \"unicode_string_to_search\""
可以在堆上搜索unicode_string_to_search
。可以使用更简单的
s -u start_address end_address
或者s -u start_address L?length
搜索。使用
.writemem
可以把指定范围的数据保存到文件中。
参考资料
《格蠹汇编》
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-address
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-writemem--write-memory-to-file-
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/address-and-address-range-syntax
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/s--search-memory-
猜你喜欢:
VS 系列:
排错实战——解决c++编译错误:error C2059: illegal token on right side of '::'
善用 vs 中的错误列表和输出窗口,高效查找 C++ 多工程编译错误
转储文件系列:
转储系列文章总结
转储文件知多少
你需要知道的 N 种抓取 dump 的工具
你生成的转储文件有问题吗?
向大厂看齐!为自己的程序增加自动转储的功能!
内核转储,开抓啦!
蓝屏(BSOD)转储设置,看本文就够了!
系统蓝屏的几种姿势,确定不了解下么?
本地内核调试环境搭建,就这么简单!
双机内核调试 101
使用 VMware + win10 + VirtualKD + windbg 从零搭建双机内核调试环境
使用 VMware + win10 + vs2019 从零搭建双机内核调试环境
本地内核调试神器 —— livekd 使用总结
调试系列:
调试实战——你知道怎么使用DebugView查看调试信息吗?
调试实战——程序CPU占用率飙升,你知道如何快速定位吗?
调试实战——崩溃在ComFriendlyWaitMtaThreadProc
调试实战——使用windbg调试崩溃在ole32!CStdMarshal::DisconnectSrvIPIDs
调试实战——调试PInvoke导致的内存破坏
调试实战——调试excel启动时死锁
调试实战——调试DLL卸载时的死锁
调试实战——调试TerminateThread导致的死锁
排错系列:
排错实战——1分钟解救 run 不出来的 Autoruns
排错实战——VS清空最近打开的工程记录
排错实战——拯救加载调试符号失败的IDA
排错实战——你知道拖动窗口时只显示虚框怎么设置吗?
排错实战——解决Tekla通过.tsep安装插件失败的问题
排错实战——使用process explorer替换任务管理器
排错实战——通过对比分析sysinternals事件修复程序功能异常