Windbg Script---从.NET应用程序检索查询/存储过程
好的,假如正在调试一个.NET应用程序。您需要找出从访问数据库的线程执行的查询和存储过程,但是您不知道如何。好消息!这已经不是问题了!此脚本显示与SQLCommand或OracleCommand对象关联的所有查询或存储过程。此外,您可以单击它以获取更多详细信息和使用它的线程。。
以下是截图:
Source code for GET_SQLCOMMAND.TXT:
$ $$ $$ ============================================================================= $$ It shows the SQL commands from a .NET application. It gives you detailed information $$ and the threads using the query/stored procedure you selected. $$ Useful for Oracle and SQL Server. $$ $$ Compatibility: Win32. $$ $$ Usage: Use $$>< to run the program. $$ $$ Requirements: Public symbols. $$ $$ If necessary change the filename below to include your path and filename. $$ By default it uses the WinDbg path and the default file name is GET_SQLCOMMAND.TXT $$ $$ Roberto Alexis Farah $$ Blog: http://blogs.msdn.com/debuggingtoolbox/ $$ $$ All my scripts are provided "AS IS" with no warranties, and confer no rights. $$ ============================================================================= $$ ad /q * r @$t0 = 0 r @$t1 = 0 .printf /D "<b>\nClick on the queries/stored procedures below to get more details and to find out the threads using it.\n\n</b>" .block { .block { as ${/v:ScriptName} MYSCRIPTS\\GET_SQLCOMMAND.TXT } .block { as SQLCommand .block { !DumpObj poi(@$t0+0x10) !DumpObj @$t0 !GCRoot @$t0 } } .block { as OracleCommand .block { !DumpObj poi(@$t0+0x14) !DumpObj @$t0 !GCRoot @$t0 } } } .foreach(obj {!dumpheap -short -type System.Data.SqlClient.SqlCommand } ) { r @$t1 = 1 .printf /D "<link cmd=\"r @$t0 = ${obj}; ${SQLCommand} ;ad ${/v:ScriptName}; $$><${ScriptName}\"><b>%mu</b></link>\n\n", poi(${obj}+0x10)+0xc } .foreach(obj {!dumpheap -short -type System.Data.OracleClient.OracleCommand } ) { r @$t1 = 1 .printf /D "<link cmd=\"r @$t0 = ${obj}; ${OracleCommand} ;ad ${/v:ScriptName}; $$><${ScriptName}\"><b>%mu</b></link>\n\n", poi(${obj}+0x14)+0xc } .if(0 = @$t1) { .printf /D "<b>\nNo SQL commands found.\n</b>" }
在WinDbg中显示和搜索std::map内容
我们希望在WinDbg中自动显示、搜索和过滤std::map对象。std::vectors的脚本相对简单,因为vectors中数据的平面结构;map是更复杂的野兽。
具体地说,Visual C++ STL中的映射是作为红黑树实现的。每个树节点都有三个重要的指针:左指针、右指针和父指针。此外,每个节点都有一个Myval字段,其中包含std::对以及节点表示的键和值。
迭代树结构需要递归,WinDbg脚本没有任何语法来定义函数。但是,我们可以递归地调用脚本——允许脚本包含$$>a<命令,该命令使用不同的参数集再次调用脚本。脚本的路径也可以在${$arg0}中找到。
当递归调用脚本时,伪寄存器的值(如$t0)将被递归调用破坏。当我偶然发现.push和.pop命令时,我正处于动态分配内存或调用shell进程来存储和加载变量的边缘,这两个命令分别存储和加载寄存器上下文。这些是递归WinDbg脚本必须的。
好,假设您想显示std::map<int,point>中键小于或等于2的值。
0:000> $$>a< traverse_map.script my_map -c ".block { .if (@@(@$t9.first) <= 2) { .echo —-; ?? @$t9.second } }"
size = 10
—-
struct point
+0x000 x : 0n1
+0x004 y : 0n2
+0x008 data : extra_data
—-
struct point
+0x000 x : 0n0
+0x004 y : 0n1
+0x008 data : extra_data
—-
struct point
+0x000 x : 0n2
+0x004 y : 0n3
+0x008 data : extra_data
对于每个pair(存储在$t9伪寄存器中),块检查第一个组件是否小于或等于2,如果小于或等于2,则输出第二个组件。
接下来是剧本。注意,它比我们对向量的处理要复杂得多,因为它本质上是用一组不同的参数调用自己,然后递归地重复。
.if ($sicmp("${$arg1}", "-n") == 0) {
.if (@@(@$t0->_Isnil) == 0) {
.if (@$t2 == 1) {
.printf /D "<exec cmd=\"db %p L10\">%p</exec>\n", @$t0, @$t0
.printf "key = "
?? @$t0->_Myval.first
.printf "value = "
?? @$t0->_Myval.second
} .else {
r? $t9 = @$t0->_Myval
command
}
}
$$ Recurse into _Left, _Right unless they point to the root of the tree
.if (@@(@$t0->_Left) != @@(@$t1)) {
.push /r /q
r? $t0 = @$t0->_Left
$$>a< ${$arg0} -n
.pop /r /q
}
.if (@@(@$t0->_Right) != @@(@$t1)) {
.push /r /q
r? $t0 = @$t0->_Right
$$>a< ${$arg0} -n
.pop /r /q
}
} .else {
r? $t0 = ${$arg1}
.if (${/d:$arg2}) {
.if ($sicmp("${$arg2}", "-c") == 0) {
r $t2 = 0
aS ${/v:command} "${$arg3}"
}
} .else {
r $t2 = 1
aS ${/v:command} " "
}
.printf "size = %d\n", @@(@$t0._Mysize)
r? $t0 = @$t0._Myhead->_Parent
r? $t1 = @$t0->_Parent
$$>a< ${$arg0} -n
ad command
}
特别值得注意的是,as命令配置了一个别名,然后递归调用使用该别名为映射的每个元素调用一个命令块;比较字符串的$sicmp函数;以及输出DML块的.printf/D函数。最后,当_Left或_Right等于树的根时,递归终止(在本例中就是这样实现树的)。
Windbg Script---挖掘调用堆栈
Windbg有很多命令和命令变体,因此有时您可能会忘记其中的一些命令,比如当您需要挖掘调用堆栈以提取更多信息时。或者您可能记得获取调用堆栈详细信息的命令,但您需要确保获得整个堆栈。或者,您可能需要一种快速挖掘堆栈的方法,首先从帧中获取信息,然后从局部变量获取信息,而无需花费太多时间键入命令或使用箭头键。
如果你在下面场景中看到你自己需要的,这个脚本就是为你准备的!
此脚本使您能够从调用堆栈快速获取以下信息:
- ANSI字符串。
- Unicode字符串。
- 符号。
- 指针引用。
- 帧局部变量。(需要专用符号)
界面非常简单,可能不太漂亮。下面是两个截图,让您了解:
Source code - DIG_STACK.TXT:
$$ $$ ============================================================================= $$ Dig information from the current call stack: $$ - Unicode Strings $$ - ANSI Strings $$ - Symbols $$ - Pointer references $$ - Local variables by frames $$ $$ Compatibility: Win32, should work on Win64. $$ $$ Usage: $$>< to run the script. $$ $$ If necessary change the filename below to include your path and filename. $$ By default it uses the WinDbg path and the default file name is DIG_STACK.TXT $$ $$ Roberto Alexis Farah $$ Blog: http://blogs.msdn.com/debuggingtoolbox/ $$ $$ All my scripts are provided "AS IS" with no warranties, and confer no rights. $$ ============================================================================= $$ .block { as ${/v:ScriptName} MYSCRIPTS\\DIG_STACK.txt } .block { .printf /D "<link cmd=\"dpu @$csp poi(@$teb+0x4);ad ${/v:ScriptName}; $$><${ScriptName}\"><b>Unicode Strings</b></link>\n\n" .printf /D "<link cmd=\"dpa @$csp poi(@$teb+0x4);ad ${/v:ScriptName}; $$><${ScriptName}\"><b>ANSI Strings</b></link>\n\n" .printf /D "<link cmd=\"dps @$csp poi(@$teb+0x4);ad ${/v:ScriptName}; $$><${ScriptName}\"><b>Symbols</b></link>\n\n" .printf /D "<link cmd=\"dpp @$csp poi(@$teb+0x4);ad ${/v:ScriptName}; $$><${ScriptName}\"><b>Pointer References</b></link>\n\n" .printf /D "<link cmd=\"kpM 2000;ad ${/v:ScriptName}; $$><${ScriptName}\"><b>Local Variables by Frames</b></link>\n" } $$ ===========================================================================
在WinDbg中显示和搜索std::vector内容
WinDbg从来都不擅长可视化。尽管Visual Studio一直都有autoexp.dat,而且最近还出现了本机调试器可视化工具,但WinDbg用户不得不满足于转储内存区域和搜索内存来识别模式。
另一方面,如果希望简化调试过程,Visual Studio目前没有提供任何自动化机会。从Visual Studio 2012开始,不能再编写宏。WinDbg继续提供脚本支持,因此可以自动执行遇到的任何日常调试任务。如果通过了初始学习曲线并掌握了WinDbg脚本,几乎可以保证比在Visual Studio中手动遍历对象的同事获得更好更快的结果。
有一些不错的在线教程可以指导了解WinDbg脚本的基本知识,而且内置文档(debugger.chm)也有一些不错的场景。在这篇文章中,我想写一个简单的脚本,它搜索std::vector实例中匹配特定条件的对象,然后显示它们。
下面是如何使用这个脚本。假设有一个点类型的复杂对象向量。反过来,点类型有一个字段数据,该字段有一个类型为int的名为z的字段。正在查找其data.z字段等于0的点。如下:
0:000> $$>a< traverse_vector.script my_points ".block { .if (@@(@$t0->data.z) == 0) { ?? @$t0 } }"
struct point * 0x0129e4d0
+0x000 x : 0n0
+0x004 y : 0n0
+0x008 data : extra_data
脚本将$t0伪寄存器传递给向量的每个元素的命令块。在上面的命令块中,if语句将提供的表达式作为C++表达式求值(@ @运算符强制C++表达式求值器),如果它是真的,则使用“??”运算符来显示当前元素。这个??运算符只显示一个C++表达式。
以下是脚本和一些注释:
$$ save pointer to first element (current element)
r? $t0=${$arg1}._Myfirst
$$ save pointer to last element
r? $t1=${$arg1}._Mylast
$$ save first forever
r? $t2=@$t0
.while (@$t0 != @$t1)
{
.if (${/d:$arg2} == 0) {
$$ display element, no command provided
.printf "index %d, address %p\n", @@(@$t0 – @$t2), @$t2
?? @$t0
} .else {
${$arg2}
}
$$ advance current element
r? $t0=@$t0 + 1
}
最初的挑战是如何处理所有的向量元素。这依赖于向量有Myfirst和Mylast成员这一事实。完成后,可以对向量内容运行任意命令、打印元素、筛选元素、将它们存储在文件中,等等。
Windbg Script---一个从连接池里查找信息的脚本
如果您像我一样,您可能会忘记需要查找某些特定信息的类和名称空间。或者你忘了要查找的字段名。例如当我需要从System.Data.SqlClient命名空间查找信息。
此脚本从连接检索特定信息。扩展它也很容易,因此可以使用相同的方法从其他名称空间和字段中提取信息。
就个人而言,我更喜欢基于DML(调试标记语言)的方法,因为我可以使用超链接和默认脚本调用:$$><。DML方法需要更多的工作和更低级的编程,但是可以提供更好的结果。
需要使用$$<而不是$$><。
Source code for CONNECTIONS_POOL.TXT:
$$ $$ ============================================================================= $$ Verify Connections from Pool. $$ $$ Compatilibity: Win32, should work on Win64. $$ $$ Attention! For .Net Framework 2.0, edit the script and remove the clr10\\ from it so it can use the $$ sos.dll version 2.0 $$ $$ Usage: $$< to run the script. (note: Just $$< not $$><) $$ $$ Requirements: Public symbols. $$ $$ Roberto Alexis Farah $$ All my scripts are provided "AS IS" with no warranties, and confer no rights. $$ ============================================================================= $$ .shell -i - -ci ".foreach ( obj {!clr10\\sos.dumpheap -type System.Data.SqlClient.SqlInternalConnection -short} ) {!do ${obj} }" FIND "_fInPool" .shell -i - -ci ".foreach ( obj {!clr10\\sos.dumpheap -type System.Data.SqlClient.SqlConnectionPoolControl -short} ) {!do ${obj} }" FIND "_fResetConnection" .shell -i - -ci ".foreach ( obj {!clr10\\sos.dumpheap -type System.Data.SqlClient.SqlConnectionPoolControl -short} ) {!do ${obj} }" FIND "_maxPool" $$ $$ Number of Connection Objects $$ ============================ $$ !clr10\\sos.dumpheap -type System.Data.OleDb.OleDbConnection -stat $$ $$ ===================================================================