I often have a need to set a breakpoint on method, print the arguments, and break on a specific argument or continue when not matched. In native code, I would use bp <symbol> "commands". But this is a managed application, so the method is not always JITted. As such, !sos.bpmd is available; however, it does not support a commands arguments. !sosex.mbm is another option and the documentation indicates that it can accept a commands arguments. From !sosex.help mbm:
"command" - Exactly one quoted string parameter may be specified. The contents of this string will be passed on to corresponding native breakpoints as the command parameter.
For further details about these options, see the debugger documentation for the 'bp' command.
The implication is that it this command argument is passed to the native breakpoint commands, such as bp. However, it fails to parse command arguments that the same bp command is able to accept.
Below are my attempts.
0:022> $ Set a breakpoint on Foo.Bar.CBase!Foo.Bar.CBase.set_Item.
0:022> !sosex.mbm Foo.Bar.CBase!Foo.Bar.CBase.set_Item
The breakpoint could not be resolved immediately.
Further attempts will be made as modules are loaded.
Breakpoint set at Foo.Bar.CBase.set_Item(System.String, System.Object) in AppDomain 0000000001f32cc0.
0:022> $ Breakpoint set. Check the native address.
0:022> !sosex.mbl
AppDomain 0000000001eceee0
--------------------------
0 eu: disable Foo.Bar.CBase!Foo.Bar.CBase.SET_ITEM ILOffset=0: pass=1 oneshot=false thread=ANY
AppDomain 0000000001f32cc0
--------------------------
0 e : disable Foo.Bar.CBase!Foo.Bar.CBase.SET_ITEM ILOffset=0: pass=1 oneshot=false thread=ANY
Foo.Bar.CBase!Foo.Bar.CBase.set_Item(string, object)
0 e 000007ff005cc799
0:022> $ Clear the breakpoint.
0:022> $ !sosex.mbc 0
0:022> $ Set breakpoint on same address with command argument.
0:022> bp 000007ff005cc799 "as /mu ${/v:col} (#rdx+0x10); .block { .printf \"Column: %mu, Value: %mu\\n\", #rdx+10, #r8+10; .if (0 != $scmp( \"${col}\", \"opt_id\")) { gc } }"
0:022> $ Check the set breakpoint.
0:022> bl
0 e 000007ff`005cc799 0001 (0001) 0:**** "as /mu ${/v:col} (#rdx+0x10); .block { .printf \"Column: %mu, Value: %mu\\n\", #rdx+10, #r8+10; .if (0 != $scmp( \"${col}\", \"opt_id\")) { gc } }"
0:022> $ Continue execution.
0:022> g
Column: abc_id, Value: 80
Column: hoge_id, Value: N
Column: priority, Value:
Column: opt_id, Value: ZEI
000007ff`005cc799 488b542430 mov rdx,qword ptr [rsp+30h] ss:00000000`056cae00=000000013f7ed498
0:022> $ Outputs arguments and continues on non-match. Breaks on match. Expected results.
The commands argument seems to be valid and does as I desire. Such breakpoints are a frequent task, so I would much prefer to set the whole thing with a single command rather than go through the above multiple steps. As far as I can determine from the sosex.mbm documentation, I should be able to do the following:
0:022> $ Cleanup. Clear the breakpoint.
0:022> bc 0
0:022> $ Ensure that the alias is not defined.
0:022> ad /q ${/v:col}
0:022> $ Try to set the same breakpoint with sosex.mbm.
0:022> !sosex.mbm Foo.Bar.CBase!Foo.Bar.CBase.set_Item "as /mu ${/v:col} (#rdx+0x10); .block { .printf \"Column: %mu, Value: %mu\\n\", #rdx+10, #r8+10; .if (0 != $scmp( \"${col}\", \"opt_id\")) { gc } }"
syntax error
^ Quotes required in ' .printf \"Column: %mu, Value: %mu\\n\", #rdx+10, #r8+10; .if (0 != $scmp( \"${col}\", \"opt_id\")) { gc } '
It fails, indicating that quotes are required, but I cannot determine where. The syntax is a bit complicated, so lets try something a little simpler:
0:022> $ Output RDX and R8 when the breakpoint is hit.
0:022> !sosex.mbm Foo.Bar.CBase!Foo.Bar.CBase.set_Item "r #rdx; r #r8"
syntax error
^ Syntax error in '!sosex.mbm Foo.Bar.CBase!Foo.Bar.CBase.set_Item "r #rdx; r #r8"'
This time it is a syntax error, but again I cannot determine where. Lets try another one.
0:022> $ Output RDX when the breakpoint is hit, and then continue.
0:022> !sosex.mbm Foo.Bar.CBase!Foo.Bar.CBase.set_Item "r #rdx; gc"
syntax error
^ Extra character error in '!sosex.mbm Foo.Bar.CBase!Foo.Bar.CBase.set_Item "r #rdx; gc"'
Another error, but this time about an extra character, but it looks OK to me. Lets try something even easier.
0:022> $ Output RDX when the breakpoint is hit.
0:022> !sosex.mbm Foo.Bar.CBase!Foo.Bar.CBase.set_Item "r #rdx"
The breakpoint could not be resolved immediately.
Further attempts will be made as modules are loaded.
Breakpoint set at Foo.Bar.CBase.set_Item(System.String, System.Object) in AppDomain 0000000001f32cc0.
Success at last. The conclusion that I draw from this is that sosex.mbm can only accept a single command argument as long as it does not contain multiple statements. As the documentation indicates, if the single command argument is merely being passed to the native breakpoint (such as bp), then there should be no such restriction.
Is this behavior expected or am I doing something wrong? Is there another way to accomplish this? I can use the above workaround, but I often need to set such breakpoints are so would much rather find a more direct way to do this.
Please note that I am using version 4.5.0.783 of sosex. This is the latest version available for download.
You're not doing anything wrong. I can see why your command with embedded quotes would fail. SOSEX isn't terribly sophisticated about parsing the quoted string. However, your simple commands should succeed. I'll work on this and get back to you.
Related
cmp $0x27,%ecx
I am currently looking for to see what is the value of $0x27 and %ecx. What is the command I can see to find this.
In gdb you can display the value of the ecx register with p $ecx (note gdb uses a $ instead of % since it treats $ecx like one of its internal variables). You can also use info registers to see the contents of all registers.
There's nothing "inside" $0x27 - it's a literal immediate value, not a memory address. It's like C compare_into_flags(ecx, 0x27);
I came across a mind-blowing weird script that crashes the console:
set "h=/?" & call [if | for | rem] %%h%%
IF, FOR and REM aren't normal internal commands.
They use an own special parser, which possibly caused some interception errors so it crashed.
#jeb pointed out CALL doesn't execute the following special characters, but instead convert them into a "token" (version dependent):
& returns /
&& returns 1
| returns 2
|| returns 0
/? returns <
# returns +
#() returns ;
#if a==a : returns ,
#for %a in () do : returns +
#rem : returns -
However, even though they have unique parsers, it still doesn't explain why they all crash. So I did some testing:
Remove call
C:\>set "h=/?" & for %h%
%%h%% was unexpected at this time.
Change the command to something else. (I tried all other internal commands, none works)
Seperate two commands:
C:\>set "h=/?"
C:\>call for %%h%%
--FOR help message--
Add #
C:\>set "h=/?" & call for #%%h%%
CRASH!!!
Surround the scriptblock by ()
C:\>set "h=/?" & call for (%%h%%)
CRASH!!!
Summary of question:
What role does call play?
What caused the parser to crash?
The CALL is necessary to start a second round of the parser.
But there is a small bug (or more), in that phase it's not possible to execute any of the special commands or using &, |, &&, ||, redirection or command blocks.
The cause seems to be, that the parser build internally a token graph, replacing the special things into some kind of token values.
But with CALL the executer doesn't know anymore how to handle them.
This code tries to execute a batch file, named 3.bat !!!
(The name can be different, depending on the windows version)
set "cmd=(a) & (b)"
call %%cmd%%
But in your sample, the help function is triggered on a non executable token.
That seems to be the final death trigger for the executer to be completely out of sanity.
Summary of Research:
Calling linefeeds \n or FOR, IF & REM's help function crashes cmd, exiting with ERRORLEVEL -1073741819 aka 0xC0000005, which indicates an access violation error.
First, the cmd parser tries to start werfault to terminate the process.
If you prematurely terminate werfault, an error message will appear!
Access violation error:
The instruction at 0x00007FF7F18E937B referenced memory at 0x0000000000000070. The memory could not be read.
It is conjectured that if, for and rem uses special parsers, but when the help function is triggered by call, a non-command token is returned, which crashes the cmd parser.
Sources:
Why I can't CALL "IF" and "FOR" neither in batch nor in the cmd?
CALL me, or better avoid call
Limit CMD processing to internal commands, safer and faster?
In short I want to create a breakpoint that will save part of memory to file and continue, something like this:
bp mymodule!MyReader::issueRead+0x2e ".writemem C:\writemem\write_$t1 rdx L r8; g"
$t1 is a counter incremented in other breakpoint. Problem is that $t1 (or ${$t1} is not resolved in file name and I'm ending with file called "write_$t1"
WinDbg's scripting is always a bit hacky. The following seems to work, although I didn't apply it to a breakpoint yet:
aS /c filename .printf "c:\\writemem\\write_%i", $t1; .block {.writemem ${filename} 00ba0000 L1}; ad filename
When you apply it to a breakpoint you probably have to escape the quotation marks again.
aS defines an alias, /c does that by using a command. In this case, I format a string using .printf. Then the variable filename is defined.
WinDbg expands aliases immediately, i.e. it does that at a time when filename is not defined yet. Therefore I use .block to create a new scope.
Finally, ad deletes the alias again.
For example, consider the following debugging session:
(gdb) break foo
Breakpoint 1 at 0x4004f1: file tst.c, line 5.
(gdb) run
Starting program: /tmp/tst
Breakpoint 1, foo () at tst.c:5
5 return ary[i++];
(gdb) finish
Run till exit from #0 foo () at tst.c:5
Value returned is $1 = 1
(gdb) cont
Continuing.
Breakpoint 1, foo () at tst.c:5
5 return ary[i++];
(gdb) finish
Run till exit from #0 foo () at tst.c:5
Value returned is $2 = 3
After executing a finish command, I get the return value assigned to a
convenience variable (e.g. $1 or $2). Unfortunately, every time the command
is executed, the value is assigned to a different variable. That's the problem,
I cannot write a script which examines the returned value cause I don't know
what variable the value was assigned to.
Why do I need that? I want to set a breakpoint at a certain function but to
stop program execution only if the function has returned a specific value. Something
like this:
break foo
commands
finish
if ($return_value != 42)
continue;
end
end
So the question is: Is there any way to examine in a script the value returned
from a function?
This isn't easy to do from the gdb CLI. Maybe it is impossible purely using the traditional CLI -- because you can have inferior control commands like finish in a breakpoint's commands. This is a longstanding gdb issue.
However, like most automation problems in gdb, it can be solved using the Python API. Now, unfortunately, this approach requires a bit of work on your part.
Essentially what you want to do is subclass the Python FinishBreakpoint class to have it do what you want. In particular you want to write a new command that will set a regular breakpoint in some function; then when this breakpoint is hit, it will instantiate your new FinishBreakpoint class. Your class will have a stop method that will use the return_value of the finish breakpoint as you like.
The first part of your question is straightforward: just use $ to access the most recent value in gdb's value history.
From GDB: Value History
The values printed are given history numbers by which you can refer to them. These are successive integers starting with one. print shows you the history number assigned to a value by printing ‘$num = ’ before the value; here num is the history number.
To refer to any previous value, use ‘$’ followed by the value's history number. The way print labels its output is designed to remind you of this. Just $ refers to the most recent value in the history, and $$ refers to the value before that. $$n refers to the nth value from the end.
But, executing commands following a finish command in a breakpoint command list may not currently be possible; see Tom Tromey's answer for a workaround.
This user guide:
http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-debugger.html
advertises:
Execution can be single-stepped: the evaluator will suspend execution
approximately after every reduction, allowing local variables to be
inspected. This is equivalent to setting a breakpoint at every point
in the program.
Yet, I can find nothing in the document that tells me how to do that. Under the heading:
2.5.2. Single-stepping
It describes how to step from breakpoint to breakpoint. But I don't want to have to set a breakpoint on every line. Was the advertisement false or is there a way to step through a program line by line?
Thanks.
After having set and reached a breakpoint, you can call :step from the debugger.
There are other single-step possibilities. Typing :help once at a breakpoint would tell you more about what you can do.
Okay, I figured it out:
ghci> :step function_name arg1 arg2
...
...
ghci> :step
...
...
ghci> :step
If you forget the function arguments, then you will get the cryptic error message:
<interactive>:138:1:
No instance for (Show (String -> Double))
arising from a use of `print'
Possible fix:
add an instance declaration for (Show (String -> Double))
In a stmt of an interactive GHCi command: print it
...which might lead you to tear your hair out. And if you want to skip to the end:
ghci> :continue