radareorg / radare2-r2pipe

Access radare2 via pipe from any programming language!
388 stars 99 forks source link

Redirecting stderr #141

Closed utkonos closed 1 year ago

utkonos commented 3 years ago

Environment

Mon Oct 11 23:03:01 UTC 2021 radare2 5.4.2 1 @ linux-x86-64 git. commit: 84e6cc6a21ec1c816d4d3eb3510d2cdc94330414 build: 2021-09-20__04:53:52 Linux x86_64 1.6.4

Description

When running an analysis command, aaa, for example, a single \n is sent to stderr no matter what. Also, if the r2 command has any error output, that is also sent to stderr. Looking at a very old issue: https://github.com/radareorg/radare2-r2pipe/pull/13 This output has been thought about in the past. What is the best way to capture this output to a variable? Because it is not setup in the subprocess Popen as stderr=PIPE, it's unclear how you want this used.

Please pardon my confusion here.

I have tried to use a context manager like so:

with contextlib.redirect_stdout(io.StringIO()) as f:
    r2.cmd('aaa')
s = f.getvalue()

However, in the above example, the stderr is not redirected properly. Careful reading of the documentation shows the following: "It also has no effect on the output of subprocesses." https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stderr

What would you suggest for capturing this stderr?

An example I have has an output of:

Cannot find basic block for switch case at 0x0047f367 bbdelta = 7
Cannot find basic block for switch case at 0x00446377 bbdelta = 7

I would like this output captured.

Test

import r2pipe
r2 = r2pipe.open('c6010097124a8df9338ef9ba111a5a43032efc2cf298a8a87b4a9fb324013ef1')
r2.cmd('aaa')
trufae commented 3 years ago

if you want to read the stderr of the process you need to check the V bindings, its the only place i implemented this feature half year ago. nobody showed interest, so i didnt went for other bindings, it should be doable to have it for python and nodejs, but that implies using and async api because stderr messages are captured asyncroously.

you can read more about this in here https://github.com/radare/v-r2pipe/blob/master/examples/errmsg.v

https://github.com/radare/v-r2pipe/blob/master/r2pipe.v#L111

I named this thing "r2pipe-side"

utkonos commented 3 years ago

This would definitely be a useful feature for the Python bindings.

trufae commented 3 years ago

indeed. ill add it to my todo list, feel free to implement it if you have some spare time. Thanks for reporting

trufae commented 2 years ago

i'm changing the code to use R_LOG apis, which permit hooking log messages at api level and configuration options to change its behaviour. but that transition not yet fully covered. maybe for 5.8

utkonos commented 2 years ago

I had found a workaround a while ago and had not revisited this issue to mention it. But a change that can be configured would be excellent. Thanks for that!

This Python package does the trick nicely:

https://github.com/minrk/wurlitzer

Install

pip install wurlitzer

Usage

Capture stdout/stderr in pipes:

from wurlitzer import pipes

with pipes() as (out, err):
    call_some_c_function()

stdout = out.read()

Capture stdout/stderr in StringIO:

from io import StringIO
from wurlitzer import pipes, STDOUT

out = StringIO()
with pipes(stdout=out, stderr=STDOUT):
    call_some_c_function()

stdout = out.getvalue()

Forward C-level stdout/stderr to Python sys.stdout/stderr, which may already be forwarded somewhere by the environment, e.g. IPython:

from wurlitzer import sys_pipes

with sys_pipes():
    call_some_c_function()

Or even simpler, enable it as an IPython extension:

%load_ext wurlitzer

To forward all C-level output to IPython during execution.