The Magic CmdLine and how I got it back
users.softlab.ece.ntua.gr
users.softlab.ece.ntua.gr
$ gdb ./txr
GNU gdb [ ... ]
[ ... ]
Reading symbols from /home/kaz/txr/txr...done.
(gdb) b eval
Breakpoint 1 at 0x808f6b0: file eval.c, line 969.
(gdb) r -p '(+ 2 2)'
Starting program: /home/kaz/txr/txr -p '(+ 2 2)'
Breakpoint 1, eval (form=0xb7fb919c, env=0xb7fb92ac, ctx_form=0xb7fb919c)
at eval.c:969
969 {
(gdb) p d(form)
(+ 2 2)
$1 = void
(gdb) p d(cons(form, form))
((+ 2 2) + 2 2)
$2 = void
(gdb) p d(cdr(form))
(2 2)
$3 = void
(gdb) p d(cons(0x1, 0x1))
(0 . 0)
$4 = void
(gdb) p cons(0x1, 0x0)
$5 = (obj_t *) 0xb7fb921c
(gdb) p cons(0x9, $5)
$6 = (obj_t *) 0xb7fb920c
(gdb) p cons(0x19, $6)
$7 = (obj_t *) 0xb7fb91fc
(gdb) p d($7)
(6 2 0)
You can test functions that are not even called anywhere from the C code. The d function is such a function; it's only there for use out of GDB. (gdb) p (0)
$10 = 0
(gdb) p d(0)
nil
$11 = void
(gdb) p t
$12 = (val) 0xb7fe5eac
(gdb) p d(t)
t
$13 = void
(gdb) p typeof(t)
$14 = (obj_t *) 0xb7fe5d3c
(gdb) p d(typeof(t))
sym
$15 = void
(gdb) p d(symbol_name(t))
"t"
$16 = void
(gdb) p d(symbol_name(0))
"nil"
$17 = void
Take out the trash, and show it: (gdb) p gc()
$18 = void
(gdb) p d($7)
#<garbage: 0xb7fb91fc>Other classics: dumping the memory of an active process (check my GitHub utils repo, I have it there) and redirecting the standard output AFTER you've started a process.
I only wish that in a site called 'Hacker News', it would be technical comments like yours at the top of this discussion. Oh well...
commands ... call d(cons(form)) call symbol_name(t) cont end
It really is a marvelous tool - unfortunately, people don't know anything except the basics (if that)
cat /proc/53165/exe > /tmp/oldBash
I would have reached for 'cp /proc/53165/exe /tmp/oldBash', instead of using cat. Is there an advantage to using cat here, or is it just the same?However, reaching for one way of doing things when the justification for the other way of doing things is because you've seen them done that way more, is not really good justification. So maybe that blurry brain is a good brain sometimes, in retrospect.
As to 'cp' vs 'cat': neither is a builtin, so they both are risky. What if you also accidentally did a rm -rf / on that system?
Mac OS X doesn't have a /proc filesystem, so I cannot test it, but from reading the man page
read -N 1000000000 /proc/53165/exe $bash
should read all of bash into $bash, if it isn't ridiculously large, and echo -nE $bash > myBash
should then complete the copy. read -N 1000000000 /proc/53165/exe $bash
That shouldn't work, due to NUL bytes.I wanted to see the command without "messing" with the execution in any way - after all, it was running fine for 6 months, why interrupt it (and face potential effort in fixing messed up state) if I could see the command "from the outside"?
And that I did :-)
If you're worried about timing out of the process being unresponsive, just C-z and then "bg" real quick. Then you can up arrow at your leisure and "fg" when you've copied the command away...
Apparently C-z followed by fg is not bulletproof...
zsh does the right thing, when you bg it back it recovers the loop as it was. Bash seems to forget about the loop and only recovers the "sleep 1", when it starts executing again it sleeps 1 and exits.
If you want to make sure it will work you should wrap it around another bash:
bash -c 'while true ; do sleep 1 ; done'
should work fine. (while true ; do sleep 1 ; done)sleep(1) is the only process that can be suspended in this command. When you "fg", the suspended sleep(1) is signaled, and resumes. And then it exits.
When you do this in a subshell (in parens), you suspend/resume the subshell in its entirety, which capture the loop construct.
Why does it work in zsh?!
――――――
I used gcore to dump the running bash process, then ran strings on the core dump, then grepped for likely command line stuff. 'while' is reasonable choice for a loop. There aren't that many strings in the dump file in any case, most of the sizeable ones are function definitions and environment variables.
Relying on gdb makes getting to the right data much more precise, but my approach took about 20 seconds.
Fantastic article though - I've learned a couple of things from it, thank you.
Some people here are already rather mean in their comments - imagine what would happen if I revealed The Magic CmdLine (TM)...
No, that privilege is reserved for the Inner Circle :-)
But I'll give you a teaser... It involved a lot of ImageMagick and "pdfimages" and "zxing" and "tesseract" and "pdfjoin" invocations... and the uploaded files were PDFs with scanned barcoded pages.
(I can imagine the gory details tbh - it sounds like there is every chance I have had a very similar command for a pretty similar purpose running for a good long while now... except mine's in a VBScript..!)
Across the road (on Reddit/programming), a commenter already described me pretty well:
"Everything about your (rather interesting) story... reaffirms everything I know about Linux sysadmins - if you encounter a problem, your go-to solution is to write a script ... Honestly, you guys would script a sandwich if you could :P"
The only thing wrong in that sentence, is that I am not a sysadmin (hence the knowledge of the Dark Arts - of GDB :-)
How that one got by me is a mystery - let's chalk it up to stress and pretend it never happened :-)
It seems that the take-away lesson from the article is that you should save it in a bash script.
A Jedi? Maybe at getting himself out of the quicksand he lays himself into ...
However, the post illustrates an important feature I often see overlooked: the systems we build on top of are not just black boxes. We should not be afraid to really understand how they work, opening up source code, asking questions like, "what the heck is this /proc/NUMBER section of the filesystem?" And I think this post illuminates that in a pretty fun way.
You're telling me that you've never - under tremendous pressure - hacked things that quickly "handle" something - and then forgotten about them till much later?
Jeez, tough crowd :-)
The extra few seconds to put this in a .sh file in the first place ... priceless. Even for the basic case of debugging it and getting it working.
I don't think in the 20-25 years I've been "doing" unix that I've ever done this (I doubt anyone that has worked with me considers me ocd fwiw but this is beyond the pale).
Behaviors like this lead to "magic" work environments and are just pants on head places to work at/with.
I would be really mad the first time I found you doing this and I'd look for a different work situation for one of us the second time :)
Thanks for the cool post.
As a sidenote to anyone reading this, I really appreciate that the Hacker News community occasionally posts and upvotes Alzheimer's articles. I read every one that I see.
Anyway, I beg that you might not use "Alzheimer's" as an expression for forgetfulness, just as you might avoid calling someone "ADD" when they multitask to a fault.
Maybe a better way to put it would be "We don't typically use 'Alzheimer's' to refer to forgetfulness because it brings up negative emotions for some people."
I guess it's yet another cultural difference I was not aware of...
Just as much as you ask for your culture to be respected, respect other cultures by acknowledging their more offensive and black humour and not taking everything personally.