How to display line numbers in stack traces on macOS? - macos

I'm compiling my command-line program using dmd 2.072.1 on OS X El Capitan and trying to get line numbers to show in stack traces when debugging with LLDB. Currently the stack traces look like this:
core.exception.RangeError#Level.d(454): Range violation
----------------
4 Game 0x000000010b108ac1 _d_arraybounds + 97
5 Game 0x000000010b07f759 Level.__array + 41
6 Game 0x000000010b0969c4 void Level.Level.GenerateBlocks() + 992
7 Game 0x000000010b094c32 Level.Level Level.Level.__ctor(Renderer.Renderer, Level.Meshes, Level.Textures, bool, boo
Is it possible to see the line numbers with LLDB? If so, how? If not, what alternatives I have when using dmd? I'm developing my D programs with Emacs, but also have Xcode installed for other languages.

I'm on MacOS and had the same problem which I fixed. When I compiled all my C source files into object files I included the -g option. Then when compiling all the object files together to produce the final executable I included the -g option as well. Then lldb showed the proper line number where the error occurred!

Related

Is it possible to backtrace "failwith" error when executing a binary?

I develop OCaml programs in Emacs, compile and run the binary in the terminal.
It is possible to backtrace "failwith" error in Emacs like this post. But most of the time, "failwith" errors are raised when I execute a binary in the terminal. There is little information except
my error message
Fatal error: exception Failure("my error message")
Does anyone know if it is possible to backtrace that? I.e., understanding which .ml files are involved in such an execution?
If you compile with debugging support, you can ask for a backtrace with OCAMLRUNPARAM=b.
$ cat bt.ml
let g x = failwith "error"
let f x = g x
let main () = f 14
let () = main ()
$ ocamlopt -g -o bt bt.ml
$ OCAMLRUNPARAM=b bt
Fatal error: exception Failure("error")
Raised at file "pervasives.ml", line 30, characters 22-33
Called from file "bt.ml", line 7, characters 9-16
In small examples like this, the inliner will make the report a little less useful. In real world programs it's probably more useful.
(But it still might not be as detailed as you'd like.)

Reason for huge size of compiled executable of Go

I complied a hello world Go program which generated native executable on my linux machine. But I was surprised to see the size of the simple Hello world Go program, it was 1.9MB !
Why is it that the executable of such a simple program in Go is so huge?
This exact question appears in the official FAQ: Why is my trivial program such a large binary?
Quoting the answer:
The linkers in the gc tool chain (5l, 6l, and 8l) do static linking. All Go binaries therefore include the Go run-time, along with the run-time type information necessary to support dynamic type checks, reflection, and even panic-time stack traces.
A simple C "hello, world" program compiled and linked statically using gcc on Linux is around 750 kB, including an implementation of printf. An equivalent Go program using fmt.Printf is around 1.9 MB, but that includes more powerful run-time support and type information.
So the native executable of your Hello World is 1.9 MB because it contains a runtime which provides garbage collection, reflection and many other features (which your program might not really use, but it's there). And the implementation of the fmt package which you used to print the "Hello World" text (plus its dependencies).
Now try the following: add another fmt.Println("Hello World! Again") line to your program and compile it again. The result will not be 2x 1.9MB, but still just 1.9 MB! Yes, because all the used libraries (fmt and its dependencies) and the runtime are already added to the executable (and so just a few more bytes will be added to print the 2nd text which you just added).
Consider the following program:
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
If I build this on my Linux AMD64 machine (Go 1.9), like this:
$ go build
$ ls -la helloworld
-rwxr-xr-x 1 janf group 2029206 Sep 11 16:58 helloworld
I get a a binary that is about 2 Mb in size.
The reason for this (which has been explained in other answers) is that we are using the "fmt" package which is quite large, but the binary has also not been stripped and this means that the symbol table is still there. If we instead instruct the compiler to strip the binary, it will become much smaller:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 1323616 Sep 11 17:01 helloworld
However, if we rewrite the program to use the builtin function print, instead of fmt.Println, like this:
package main
func main() {
print("Hello World!\n")
}
And then compile it:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 714176 Sep 11 17:06 helloworld
We end up with an even smaller binary. This is as small as we can get it without resorting to tricks like UPX-packing, so the overhead of the Go-runtime is roughly 700 Kb.
Note that the binary size issue is tracked by issue 6853 in the golang/go project.
For instance, commit a26c01a (for Go 1.4) cut hello world by 70kB:
because we don't write those names into the symbol table.
Considering the compiler, assembler, linker, and runtime for 1.5 will be
entirely in Go, you can expect further optimization.
Update 2016 Go 1.7: this has been optimized: see "Smaller Go 1.7 binaries".
But these day (April 2019), what takes the most place is runtime.pclntab.
See "Why are my Go executable files so large? Size visualization of Go executables using D3" from Raphael ‘kena’ Poss.
It is not too well documented however this comment from the Go source code suggests its purpose:
// A LineTable is a data structure mapping program counters to line numbers.
The purpose of this data structure is to enable the Go runtime system to produce descriptive stack traces upon a crash or upon internal requests via the runtime.GetStack API.
So it seems useful. But why is it so large?
The URL https://golang.org/s/go12symtab hidden in the aforelinked source file redirects to a document that explains what happened between Go 1.0 and 1.2. To paraphrase:
prior to 1.2, the Go linker was emitting a compressed line table, and the program would decompress it upon initialization at run-time.
in Go 1.2, a decision was made to pre-expand the line table in the executable file into its final format suitable for direct use at run-time, without an additional decompression step.
In other words, the Go team decided to make executable files larger to save up on initialization time.
Also, looking at the data structure, it appears that its overall size in compiled binaries is super-linear in the number of functions in the program, in addition to how large each function is.

Can I prevent debugger from stepping into Boost or STL header files?

I'm using Qt Creator with gdb to debug my C++ code on a Linux Platform. Whenever I use a boost::shared_ptr or the like, the debugger steps into the header files containing the boost implementation (i.e. /usr/include/boost/shared_ptr.hpp). I would like to ignore these files in terms of debugging and simply step over them. I know that I can step out as soon as it reaches one of these files, but it would be much easier to debug without doing so several times per debugging session.
I'm using the gcc compiler (g++), running on OpenSuSE Linux 11.2 with QtCreator 2.2 (which uses gdb as the debugger.)
Edit to add: The question is geared toward Boost files, but could also apply toward STL files as well.
GDB without stepping into STL and all other libraries in /usr:
Put the following in your .gdbinit file. It searches through the sources that gdb has loaded or will potentially load (gdb command info sources), and skips them when their absolute path starts with "/usr". It's hooked to the run command, because symbols might get reloaded when executing it.
# skip all STL source files
define skipstl
python
# get all sources loadable by gdb
def GetSources():
sources = []
for line in gdb.execute('info sources',to_string=True).splitlines():
if line.startswith("/"):
sources += [source.strip() for source in line.split(",")]
return sources
# skip files of which the (absolute) path begins with 'dir'
def SkipDir(dir):
sources = GetSources()
for source in sources:
if source.startswith(dir):
gdb.execute('skip file %s' % source, to_string=True)
# apply only for c++
if 'c++' in gdb.execute('show language', to_string=True):
SkipDir("/usr")
end
end
define hookpost-run
skipstl
end
To check the list of files to be skipped, set a breakpoint somewhere (e.g., break main) and run gdb (e.g., run), then check with info sources upon reaching the breakpoint:
(gdb) info skip
Num Type Enb What
1 file y /usr/include/c++/5/bits/unordered_map.h
2 file y /usr/include/c++/5/bits/stl_set.h
3 file y /usr/include/c++/5/bits/stl_map.h
4 file y /usr/include/c++/5/bits/stl_vector.h
...
Its easy to extend this to skip other directories as well by adding a call to SkipDir(<some/absolute/path>).
gdb is scriptable. it has while, if, variables, shell subcommands, user-defined functions (define) etc etc. it has python interface for scriptability.
With a bit of work, you can to make gdb script along these lines:
define step-bypass-boost
step
while 1
use "info source", put current source file into variable
if source file does not match */boost/* then
break-loop
end
step
end
end
or find whether somebody already made such script
Instead of doing s (step), you can
b on first line of your function where you want to stop (b Class::method, or b file.cpp:line),
then c.
gdb will bypass the boost code and break at the point given in b, where you want it
this works but can seem tedious. it's matter of habit. becomes easier with repetition.
msvc behaves similar to gdb
From https://stackoverflow.com/a/31629136/5155476:
I had this same need. I extended the 'skip' command in gdb to support a new type 'dir'. I can now do this in gdb:
skip dir /usr
and then I'm never stopped in any of my 3rd party headers.
Here's a webpage w/ this info + the patch if it helps anyone: info & patch to skip directories in GDB

What is wrong with this Fortran '77 snippet?

I've been tasked with maintaing some legacy fortran code, and I'm having trouble getting it to compile with gfortran. I've written a fair amount of Fortran 95, but this is my first experience with Fortran 77. This snippet of code is the problematic one:
CHARACTER*22 IFILE, OFILE
IFILE='TEST.IN'
OFILE='TEST.OUT'
OPEN(5,FILE=IFILE,STATUS='NEW')
OPEN(6,FILE=OFILE,STATUS='NEW')
common/pabcde/nfghi
When I compile with gfortran file.FOR, all lines starting with the common statement are errors (e.g. Error: Unexpected COMMON statement at (1) for each following line until it hits the 25 error limit). I compiled with -Wall -pedantic, but fixing the warnings did not fix this problem.
The crazy thing is that if I comment out all 4 lines starting with IF='TEST.IN', the program compiles and works as expected, but I must comment out all of them. Leaving any of them uncommented gives me the same errors starting with the common statement. If I comment out the common statement, I get the same errors, just starting on the following line.
I am on OS X Leopard (not Snow Leopard) using gfortran. I've used this very system with gfortran extensively to write Fortran 95 programs, so in theory the compiler itself is sane. What the hell is going on with this code?
Edit: Compiling with g77 gives:
test.FOR: In program `MAIN__':
test.FOR:154:
IFILE='TEST.IN'
1
test.FOR:158: (continued):
common/pabcde/nfghi
2
Statement at (2) invalid in context established by statement at (1)
Er, what context is established at (1)?
I don't think you can put COMMON statements below the executable statements in FORTRAN 77, see the specification, Sec. 3.5.
Just move the COMMON statement close to the beginning of the procedure, before any executable statement.

How can I get an assembly language listing of my Arduino Sketches on Windows?

I would like to be able to see an assembly language listing of my Arduino sketches. How can I achieve this?
Update: I am running the Arduino Software on a Windows machine.
One way to do this is to use avr-objdump on the .elf file created by the build. For example, on OS X I can do this:
$ cd ~/arduino-0015/examples/Digital/Blink/applet
$ avr-objdump -d Blink.elf
(Your path on Windows may be different, obviously.) This produces a disassembly of the code, part of which will look something like this:
0000013a <main>:
13a: 0e 94 3e 01 call 0x27c <init>
13e: 0e 94 97 00 call 0x12e <setup>
142: 0e 94 80 00 call 0x100 <loop>
146: fd cf rjmp .-6 ; 0x142 <main+0x8>
If you are using Linux, you can follow this tutorial on how to compile for the Arduino without the IDE.
Once you do that, you can get an assembly listing by running gcc with the -s flag.
The following (hacky) steps will provide assembly language listings of Arduino sketches and associated libraries on Windows:
Download (and rename) the Arduino Windows command line batch files into the directory containing your sketch (the .pde file)
Set up the required environment variables as specified in the above linked page
Add -S to the abuild_gcc_opts variable in abuild.bat (line 158)
Run abuild -r -c <pde_filename>
Expect to get the following warnings and errors, which you can ignore:
... warning: #warning "This file has been moved to <util/delay.h>."
.\obj\<pde_filename>.cpp.o: file format not recognized: treating as linker script
.\obj\<pde_filename>.cpp.o:1: syntax error
The assembly language listings can be found in the .o files in the created obj directory. For example the listing for the sketch itself is in obj\<pde_filename>.cpp.o
The -S (not s) flag show the c code as well.Also know as mixed listing:
linux: (.arduino/preferences.txt: delete_target_folder=false)
$ cd /tmp/buildxxxx.tmp
$ avr-objdump -dS Blink.cpp.elf
int main(void)
{
init();
2f4: 8a df rcall .-236 ; 0x20a <init>
...
Arduino compilation is set up with link-time optimization (LTO), and the assembly files created during C compilation don't contain any assembly code - just the intermediate representation used later in the LTO stage to generate actual assembly code. We want the latter.
It so happens, rather nicely, that the very last time assembler is invoked in the entire compilation process, is to take the output from the LTO stage, and turn it into a whole-application object file.
To get at this assembly file, do the following:
Open %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\avr\1.8.3\platform.local.txt (create it if it doesn't exist). Add the following line
compiler.c.elf.extra_flags=-save-temps=obj -fverbose-asm
If there already is such a line there, just add the above options (to the right of =).
Note: If you can't find the folder, look for the following files: boards.txt, platform.txt, programmers.txt. If you find these files in multiple locations, the one you want should be inside your home directory (i.e. not in Program Files or /Applications or /usr), and should be correlated at least partially to the version of Arduino you're using (should you have multiple versions installed).
Build the sketch again.
Look for a file named %TEMP%\cc*.ltrans.s. Each build will create a new such file. If you sort them by timestamp, the newest one is for the most recent build.
Note: The environment variables, enclosed by % signs, are valid in the Windows dialog boxes. You can directly paste such a path into e.g. an Open or Save As... dialog box, and it'll work. Paths with globs (*) will act as filters (IIRC).
To know which exact cc*.s file was generated, add the verbose option to the final binary generator pass:
compiler.c.elf.extra_flags=-save-temps=obj -fverbose-asm -v
Rebuild, and copy-paste all the output from the compilation status window in Arduino to e.g. Notepad++, and search for ar.exe. The sole line that contains ar.exe ends with the name of this cc file:
c:/users/[...omitted...]/avr/bin/as.exe -mmcu=avr6 -mno-skip-bug
-o C:\Users\[...]\AppData\Local\Temp\cc3XhU2F.ltrans0.ltrans.o
C:\Users\[...]\AppData\Local\Temp\cc3XhU2F.ltrans0.ltrans.s
How does the file look? This is a short extract, just to show that both the C source and the assembly are intermingled - and this represents exactly the binary embedded in the .hex file and sent to the target.
.LBE33:
.LBE32:
.LBB34:
.LBB35:
; C:\Users\[...]\Documents\Arduino\sketch_jul01a\sketch_jul01a.ino:29: pinMode(ledPin, OUTPUT);
.file 4 "C:\\Users\\[...]\\Documents\\Arduino\\sketch_jul01a\\sketch_jul01a.ino"
.loc 4 29 0
ldi r22,lo8(1) ; ,
ldi r24,lo8(13) ; ,
call pinMode ;
.LVL55:
; C:\Users\[...]\Documents\Arduino\sketch_jul01a\sketch_jul01a.ino:30: pinMode(relayPin, OUTPUT);
.loc 4 30 0
ldi r22,lo8(1) ; ,
ldi r24,lo8(12) ; ,
call pinMode ;
.LVL56:
; C:\Users\[...]\Documents\Arduino\sketch_jul01a\sketch_jul01a.ino:31: pinMode(ldrPin, INPUT);
.loc 4 31 0
ldi r22,0 ;
ldi r24,lo8(54) ; ,
call pinMode ;
.LVL57:
.LBE35:
.LBE34:
.LBB36:
.LBB37:
This approach also works on Linux and MacOS, except that the paths are slightly different, and path expansion uses different syntax. The two files of concern still are platform.local.txt - a file you must create, since it doesn't exist at first, and the ${TEMP}/cc*.ltrans.s files.

Resources