How to print wstring in gdb - gdb

How can I print wstring in gdb?

call printf %ls only works sometimes, but to get it to work at all in gdb 6.3 you need the void cast and linefeed \n shown here:
call (void)printf("\"%ls\"\n",str.c_str())
here is a more reliable command you can put in your .gdbinit that also shows non-ASCII code points:
define wc_print
echo "
set $c = (wchar_t*)$arg0
while ( *$c )
if ( *$c > 0x7f )
printf "[%x]", *$c
else
printf "%c", *$c
end
set $c++
end
echo "\n
end
just enter wc (short for wc_print) with either a std::wstring or wchar_t*.
More detail at http://www.firstobject.com/wchar_t-gdb.htm

Suppose you've got a std::wstring str. The following should work in gdb:
call printf("%ls", str._M_data())
(The -l option in printf makes it a long string, and I believe you need the "call" statement because the ordinary gdb printf doesn't like that option.)

I did some research, and this is gdb PR716, PR1998, PR2264. Apparently this is an often-requested feature that is not yet implemented.

Related

shell wrapper to restart script based on its output

I've had a bit of shell scripting practice reading piped input from other programs, but am unsure how to approach this problem.
THE BACKSTORY
A program robinbotter whose internals I can't really fix/modify takes its input from files equities.sym and blacklist.sym, each simple text files containing one ticker symbol per line.
When it runs okay, its output produces:
...
Downloading instruments: [ AFL KELYB LFUS ]
...
When it breaks due to internal bugs,
...
Downloading instruments: [ AFL KELYB LFUS LNVGY
and halts there, with no further output, yielding exit code 0 like in the okay case (unfortunately).
The ticker symbols are printed out with slight delay--no newlines in between--while the program is processing them.
When it hits LNVGY or unpredictably any other many possibilities, somehow it can't handle or at least skip them, instead crashing with no proper exception nor error code.
THE QUESTION
I'm trying to write a minimalistic wrapper script in BASH (eg. retryRB.sh ./robinbotter) which:
Somehow monitors the live unbuffered output of robinbotter, using a regex or other method to detect when output of a line containing "Downloading instruments: [ " doesn't end with "]" before the program ends. In which case:
Take the last symbol printed out (eg. LNVGY) which crashes the program, and append it to the bottom of file blacklist.sym. Like with
echo $lastSymbol >> blacklist.sym
Restart the program robinbotter, retaining its original command-line parameters: $#
I am familiar with tools like awk and sed, and would be open to building a short solution in Ruby if Bash doesn't cut it.
Here you have a Bash version of a code that imitates what your binary does.
then you have a wrapper which logs when the apps successfully completes, and also, when it fails. On failure, it also appends the last item printed, as you can see in the images below ( in this case Im hard-coding a failure on Bomb! and Boom! but you get the idea):
main.sh
#!/bin/bash
some=('Pera' 'Manzana' 'Frutilla' 'Durazno' 'Banana' 'Lechuga' 'Sandia' 'Papa' 'Melon' 'Milanesa' 'Bomb!' 'Boom!')
printf 'Downloading instruments: [ '
for (( i=1 ; i < 5 ; i++ )) {
item=${some[$( shuf -i 0-$(( ${#some[#]} - 1 )) -n 1 )]}
printf "$item"
[[ $item == 'Bomb!' || $item == "Boom!" ]] && exit || printf "$item"
[[ $i -lt 4 ]] && printf ' '
}
printf ' ]'
wrapper.sh
#!/bin/bash
while :
do
res=$( ./main.sh )
[[ ! "$res" =~ \[[^]]*\] ]] && printf "Failure : ${res##*[\[ ]}" || printf "Success"
printf '\n'
sleep 1
done
You can test these scripts and then put your binary in place of main.sh.
Regards!

gdb user-defined functions: how to pass complex argument?

I want to examine several QString variables,
so I found macros for this in Internet:
define printqstring
printf "(QString)0x%x (length=%i): \"",&$arg0,$arg0.d->size
set $i=0
while $i < ($arg0).d->size
set $c=$arg0.d->data[$i++]
if $c < 32 || $c > 127
printf "\\u0x%04x", $c
else
printf "%c", (char)$c
end
end
printf "\"\n"
end
But when I try to use it, I got such error:
(gdb) printqstring ((MyWidget *)0xd98cb0)->caption_
A syntax error in expression, near `,((MyWidget.d->size'.
if I try to use commands from macros by hand, they work fine:
(gdb) printf "(QString)0x%x (length=%i): \"",&((MyWidget *)0xd98cb0)->caption_,((MyWidget *)0xd98cb0)->caption_.d->size
(QString)0xd98ccc (length=3)
So how can I pass such complex argument to gdb macros?
Unfortunately gdb always divides input to a user-defined function at any space character, even if that character is inside parentheses or something like that.
So you can just make sure you don't use any spaces in the argument you want to pass:
(gdb) printqstring ((MyWidget*)0xd98cb0)->caption_
^~~ removed space
I don't know of any good way to make this more convenient and allow spaces.

Why this single and double quote make so much difference in output

Why outputs of these two commands differ?
cat config.xml|perl -ne 'print $1,"\n" if /([0-9\.]+):161/'
cat config.xml|perl -ne "print $1,"\n" if /([0-9\.]+):161/"
First works as expected printing out matched group while seconds prints whole line.
I see two main things wrong with your command.
First off, double quotes allow shell interpolation, and $1 will be taken for a shell variable and replaced. Since it unlikely exists, it will be replaced with an empty string. So instead of print $1, you get print, which is shorthand for print $_, and is probably why the entire line prints.
Second, you have unescaped double quotes inside your command, so you are in fact passing three strings to Perl:
print ,
\n
if /(....)/
As for why or how this works with your shell, I don't know, since I do not have access to your OS, nor know which one it is. In Windows, I get a Perl bareword warning for n (Unquoted string "n" may clash with future reserved word at -e line 1.) which means that the \n is interpreted as a string. Now, here's the tricky part. What we get is this:
print , \n if /.../
Which means that \n is no longer an argument to print, it is a statement that comes after print and it is in void context, so it gets ignored. We can see this by this warning (which I had to fake in my shell):
Useless use of single ref constructor in void context at -e line 1.
(Note that you do not get these warnings as you do not use warnings -- the -w switch)
So what we are left with is
print if /.../
Which is exactly the code for the behaviour you described: It prints the whole line when a match is found.
What you can do to visualize the problem in your shell is add the -MO=Deparse switch to your one-liner, as shown here:
C:\perl>perl -MO=Deparse -ne"print ,"\n" if /a/"
LINE: while (defined($_ = <ARGV>)) {
print($_), \'n' if /a/;
}
-e syntax OK
Now we can clearly see that the print statement is separated from the newline, and that the newline is a reference to a string.
Solution:
However, your code has other problems, and if done right you can avoid all the shell difficulties. First, you have a UUOC (Useless Use of Cat). A file argument can be given to perl when using the -n switch on the command line. Secondly, you do not need to use variables for this, you can simply print the return value of your regex:
perl -nlwe 'print for /(...)/' config.xml
The -l switch will handle newlines for you, and in this case add newline to the print. The for is necessary to avoid printing empty matches.
Inside double quote, some stuffs are substituted ($variable, `command`, ..). While inside single quote, they are remained as is.
$ echo "$HOME"
/home/falsetru
$ echo '$HOME'
$HOME
$ echo "`echo 1`"
1
$ echo '`echo 1`'
`echo 1`
Nested quotes:
$ echo ""hello""
hello
$ echo '"hello"'
"hello"
$ echo "\"hello\""
"hello"
Escape double quotes, $ to get same result:
cat config.xml | perl -ne "print \$1,\"\n\" if /([0-9\.]+):161/"
Two things:
Nested quotes.
Variables expand differently.
The first command has one string that happens to contain some double quotes. The variable is not expanded.
The second command has two strings with an unquoted \n in between. The variable is expanded.
Let's say $1 contains "blah"
The first passes this string to perl:
print $1,"\n" if /([0-9\.]+):161/
the second, this:
print blah,\n if /([0-9\.]+):161/

Checking for <cr> in if statement in Windows Command Prompt

As per my corrected answer to another question it seems that Windows XP doesn't strip <cr>'s from for /f output.
How can I test for the existance of <cr>'s in the output?
What I currently have is:
for /f "tokens=1,2,3* %%a in ('ping -n 1 example.com') do (
if not "x%%a"=="¶" ( echo %%a ) else ( echo.>null )
)
but no matter what I do:
2) delims= ¶
3) if not "x%%a"==""
4) if not "x%%a"=="
"
5) if not "x%%a"==" "
... I cannot seem to get the if to match the in the var with any combination of the above.
Using the FOR statement, there really is no way that I know of to consistently check for empty lines.
The FOR statement actually skips blank lines, which is what a carriage-return only is, just a blank line.
If it is able to check, you just need to check for an empty string. Like if "%%x"=="" echo Yes!
But try this at the command line:
for /f "tokens=*" %x in ('for /?') do #echo %x
Then compare it to the output of:
for /?
And you will see that (usually*) FOR does not include empty lines.
*I have seen it happen on occasion (usually when it's a pain), but I've never been able to reproduce it consistently.
Edited Answer

How can I make GDB print a string literally (without escaping)?

I have a big, long string that I want to capture to a file. I can use logging to get most of the way there:
set logging on
set logging file gdb.log
…but if I use p or x/s to print the string, quotes and junk are all escaped. How can I get the string as-is?
For a really large string you can also use:
(gdb) set variable $s = MY_STRING
(gdb) dump binary memory FILE $s $s + (size_t)strlen($s)
which can be easily adapted to handle buffers with null bytes. Also the content of FILE would never contain anything other than the string.
Ah, I totally forgot about printf:
printf "%s\n", some_string