Comments (13)
Got answer, let's close now.
from mri.
One other caveat I will mention is that if your program does direct stdout to GDB via MRI's semi-hosting support, the program will hang the first time it tries to write something to stdout if GDB isn't attached since it will wait for the ack back from GDB. There are hacks that you can do for a particular platform port to work around this a bit. Let me know if you want to do know more. I can point you to some hacks that I have done in the past.
from mri.
When using the semi host support, if you write to file handle number 1 then you are writing to stdout. Writes to file handle number 2 will go to stderr and reads from file number 0 come from stdin. Nothing needs to be done on the GDB side.
In the case of newlib, it already does this for us. When you call functions like printf(), several calls later down in the call stack you will see a call to the _write() sys call for file handle number 1. In the project I am currently working on, _write() is implemented like this: https://github.com/adamgreen/rugrover/blob/master/mriblue_boot/shared/newlib_retarget.c#L139 which ends up calling mriNewlib_SemihostWrite() that is nothing but a bkpt MRI_NEWLIB_SEMIHOST_WRITE
instruction that will cause a transfer into MRI to satisfy the write request via GDB's file API. The file number of 1 will be passed to GDB and it does the right thing by sending the data to stdout.
Does that help?
from mri.
When you call printf() in NuttX, where does it send the output? You want to do whatever you need to do in NuttX to get it to use the ARM semihost layer for this output as well.
from mri.
I should note that it is something you probably want to be able to turn on and off though as semihost redirection of stdout will be a lot slower than just dumping it out a serial port or other channel.
from mri.
When using the semi host support, if you write to file handle number 1 then you are writing to stdout. Writes to file handle number 2 will go to stderr and reads from file number 0 come from stdin. Nothing needs to be done on the GDB side.
In the case of newlib, it already does this for us. When you call functions like printf(), several calls later down in the call stack you will see a call to the _write() sys call for file handle number 1. In the project I am currently working on, _write() is implemented like this: https://github.com/adamgreen/rugrover/blob/master/mriblue_boot/shared/newlib_retarget.c#L139 which ends up calling mriNewlib_SemihostWrite() that is nothing but a
bkpt MRI_NEWLIB_SEMIHOST_WRITE
instruction that will cause a transfer into MRI to satisfy the write request via GDB's file API. The file number of 1 will be passed to GDB and it does the right thing by sending the data to stdout.Does that help?
Yes, very useful. I have three more questions:
- Where will gdb output for stdout(1)/stderr(2)
- Where will gdb get input for stdin(0)
- If the user doesn't input anything, does the semihost read block until user input something or return with 0 length directly
When you call printf() in NuttX, where does it send the output? You want to do whatever you need to do in NuttX to get it to use the ARM semihost layer for this output as well.
Yes, it's very easy to wrapper a special console/syslog driver on top of semihost layer. We will do that after the above questions get confirm.
I should note that it is something you probably want to be able to turn on and off though as semihost redirection of stdout will be a lot slower than just dumping it out a serial port or other channel.
It's reasonable since the trap and RSP add more overhead, but the benefit is that we can use the same physical UART port to do the debug and shell at the same time.
from mri.
- Where will gdb output for stdout(1)/stderr(2)
- Where will gdb get input for stdin(0)
- If the user doesn't input anything, does the semihost read block until user input something or return with 0 length directly
- It goes to the GDB console. If you are running GDB from the command line, then it goes to the same console where you type in GDB commands and the results come back to you. If you are running in an IDE then I am not sure. Usually there is a GDB console window in the IDE where you can type commands manually to GDB even from the IDE. It should definitely show up there and maybe the IDE can intercept it and redirect it somewhere else if the GDB MI interface flags it as debuggee output...I don't know for sure. I rarely use IDEs.
- Same as the answer to 1.
- Yes, it is blocking and as far as I can tell there is no non-blocking solution for determining if the user has typed something in GDB while the debuggee is running. What I have done in some of my programs in the past is to have my embedded program not normally read from stdin. However I have a global variable named something like g_debug that I can set to 1 from within the debugger and then the main program loop checks this global and when not 0 it runs code which outputs a debug menu to stdout and then waits for input from stdin.
Here is some example output from a recent run of my robot which dumps to stdout the number of PID updates that have occurred every 10 seconds so that I can make sure that my PID loop is running at the expected frequency. You can see where I have typed commands and CTRL+C into the same GDB console that my program also dumps this stdout to:
GNU gdb (GNU Arm Embedded Toolchain 9-2020-q4-major) 8.3.1.20191211-git
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-apple-darwin10 --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from _build/RugRover.out...
Remote debugging using :3333
0x0003c4bc in Reset_Handler ()
(gdb) c
Continuing.
1000
995
1000
1000
1000
1000
^C
Program received signal SIGINT, Interrupt.
0x00009f80 in ?? ()
(gdb) q
from mri.
Thanks for the detailed explanation. The blocking reading behavior isn't very good, we can't build a shell which could interactive through gdb console natively.
from mri.
Agreed. The only input you can type into GDB which will result in something being sent to MRI asynchronously is a CTRL+C which results in a halt :(
I have used that before to pop up a non-interactive shell. I detect that CTRL+C and the continue have been sent which means that I should pop up my debug menu as soon as I can because the user has connected to me with GDB. I just use the hooks set by mriSetDebuggerHooks() to notice that MRI has been entered/left and use that to know when I should activate the blocking debug menu.
from mri.
We will try to support shell with MRI on NuttX, let's see how we can improve the input experience and report back to you.
from mri.
Sure, could you point me to the related code? We can try how to integrate that piece of code to NuttX.
from mri.
An overview of a hack that I did recently and others in the past have been similar:
- Provide my own implementation of the semihost write function which detects that a write to stdout is being attempted and send an 'O' packet with the output to GDB instead of a "Fwrite" packet. The 'O' packet is used because it doesn't need to wait around for GDB to send 'm' packets to read out the buffer contents. It is right there in the packet (but hexadized so that it now takes 2 characters per byte).
- For each write to stdout, increment counters like here.
g_fakeAckCount
is used to account for the fact that MRI is going to want to wait for the '+' ack byte back from GDB for this 'O' packet so just pretend that you got it from GDB when MRI fetches the next character.g_ignoreAckCount
takes care of the fact that if GDB is connected then it is going to respond with a '+' ack at some point for an ack that we already faked. This global is used to know that these ack bytes should just be discarded when they are received from GDB.
That particular mriblue_boot implementation puts the outbound packets in a queue and at some point it will fill up and then the code will block. There are hacks I could do in that particular implementation to work around that but I don't have a need for that at this stage in my robot project since I always have GDB attached and I want to lose as little printf() text as possible.
from mri.
An overview of a hack that I did recently and others in the past have been similar:
- Provide my own implementation of the semihost write function which detects that a write to stdout is being attempted and send an 'O' packet with the output to GDB instead of a "Fwrite" packet. The 'O' packet is used because it doesn't need to wait around for GDB to send 'm' packets to read out the buffer contents.
I think this is a general enhancement and should be part of the mainline.
It is right there in the packet (but hexadized so that it now takes 2 characters per byte).
Yes, 'O' need convert the binary to hex which lose the performance. But, "Fwrite" packet is more slower than 'O':
- Need three trip("Fwrite"->"m"->"Frr,ee,cc")
- "m" also need convert the binary to hex
from mri.
Related Issues (12)
- UART1 has wrong Rx pin
- arm-none-eabi-gdb - unrecognized item "timeout" in "qSupported" HOT 1
- Switch license from LGPL to Apache HOT 4
- Issue trying to port DWT code from MRI to personal project. HOT 7
- Consider checking base priority in Platform_EnableSingleStep() HOT 5
- Question HOT 2
- What's MRI_THREAD_MRI meaning? HOT 6
- Source level single stepping over line of code which crashes can hang MRI
- armv7-m and armv8-m are similar in debug module structure, whether it is possible to use the same set of codes, and then use macros to control armv7-m or armv8-m HOT 1
- Add some integration guide without gcc4mbed to the README HOT 1
- Failed to build CPPUTEST HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mri.