print_backtrace_skipping_top_frames: Implementation for MSVC
parent
f8ab629986
commit
d1e7a54f3a
|
@ -31,55 +31,20 @@ fn on_panic(f fn (int) int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_backtrace_skipping_top_frames(skipframes int) {
|
pub fn print_backtrace_skipping_top_frames(skipframes int) {
|
||||||
$if mac {
|
$if windows {
|
||||||
buffer := [100]byteptr
|
$if msvc {
|
||||||
nr_ptrs := C.backtrace(*voidptr(buffer), 100)
|
if print_backtrace_skipping_top_frames_msvc(skipframes) { return }
|
||||||
C.backtrace_symbols_fd(*voidptr(&buffer[skipframes]), nr_ptrs-skipframes, 1)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
$if linux {
|
$if mingw {
|
||||||
$if !android {
|
if print_backtrace_skipping_top_frames_mingw(skipframes) { return }
|
||||||
$if glibc {
|
|
||||||
// backtrace is not available on Android.
|
|
||||||
//if C.backtrace_symbols_fd != 0 {
|
|
||||||
buffer := [100]byteptr
|
|
||||||
nr_ptrs := C.backtrace(*voidptr(buffer), 100)
|
|
||||||
nr_actual_frames := nr_ptrs-skipframes
|
|
||||||
mut sframes := []string
|
|
||||||
csymbols := *byteptr(C.backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames))
|
|
||||||
for i in 0..nr_actual_frames { sframes << tos2(csymbols[i]) }
|
|
||||||
for sframe in sframes {
|
|
||||||
executable := sframe.all_before('(')
|
|
||||||
addr := sframe.all_after('[').all_before(']')
|
|
||||||
cmd := 'addr2line -e $executable $addr'
|
|
||||||
|
|
||||||
// taken from os, to avoid depending on the os module inside builtin.v
|
|
||||||
f := C.popen(cmd.str, 'r')
|
|
||||||
if isnil(f) {
|
|
||||||
println(sframe) continue
|
|
||||||
}
|
}
|
||||||
buf := [1000]byte
|
|
||||||
mut output := ''
|
|
||||||
for C.fgets(voidptr(buf), 1000, f) != 0 {
|
|
||||||
output += tos(buf, vstrlen(buf))
|
|
||||||
}
|
|
||||||
output = output.trim_space()+':'
|
|
||||||
if 0 != int(C.pclose(f)) {
|
|
||||||
println(sframe) continue
|
|
||||||
}
|
|
||||||
println( '${output:-45s} | $sframe')
|
|
||||||
}
|
|
||||||
//C.backtrace_symbols_fd(*voidptr(&buffer[skipframes]), nr_actual_frames, 1)
|
|
||||||
return
|
|
||||||
}$else{
|
}$else{
|
||||||
C.printf('backtrace_symbols_fd is missing, so printing backtraces is not available.\n')
|
if print_backtrace_skipping_top_frames_nix(skipframes) { return }
|
||||||
C.printf('Some libc implementations like musl simply do not provide it.\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
println('print_backtrace_skipping_top_frames is not implemented on this platform for now...\n')
|
println('print_backtrace_skipping_top_frames is not implemented on this platform for now...\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn print_backtrace(){
|
pub fn print_backtrace(){
|
||||||
// at the time of backtrace_symbols_fd call, the C stack would look something like this:
|
// at the time of backtrace_symbols_fd call, the C stack would look something like this:
|
||||||
// 1 frame for print_backtrace_skipping_top_frames
|
// 1 frame for print_backtrace_skipping_top_frames
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
module builtin
|
||||||
|
|
||||||
|
fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool {
|
||||||
|
println('not implemented, see builtin_windows.v')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_backtrace_skipping_top_frames_mingw(skipframes int) bool {
|
||||||
|
println('not implemented, see builtin_windows.v')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_backtrace_skipping_top_frames_nix(xskipframes int) bool {
|
||||||
|
skipframes := xskipframes + 2
|
||||||
|
$if mac { return print_backtrace_skipping_top_frames_mac(skipframes) }
|
||||||
|
$if linux { return print_backtrace_skipping_top_frames_linux(skipframes) }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// the functions below are not called outside this file,
|
||||||
|
// so there is no need to have their twins in builtin_windows.v
|
||||||
|
fn print_backtrace_skipping_top_frames_mac(skipframes int) bool {
|
||||||
|
buffer := [100]byteptr
|
||||||
|
nr_ptrs := C.backtrace(*voidptr(buffer), 100)
|
||||||
|
C.backtrace_symbols_fd(*voidptr(&buffer[skipframes]), nr_ptrs-skipframes, 1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_backtrace_skipping_top_frames_linux(skipframes int) bool {
|
||||||
|
$if tinyc {
|
||||||
|
println('TODO: print_backtrace_skipping_top_frames_linux $skipframes with tcc fails tests with "stack smashing detected" .')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
$if !android {
|
||||||
|
$if glibc {
|
||||||
|
// backtrace is not available on Android.
|
||||||
|
//if C.backtrace_symbols_fd != 0 {
|
||||||
|
buffer := [100]byteptr
|
||||||
|
nr_ptrs := C.backtrace(*voidptr(buffer), 100)
|
||||||
|
nr_actual_frames := nr_ptrs-skipframes
|
||||||
|
mut sframes := []string
|
||||||
|
csymbols := *byteptr(C.backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames))
|
||||||
|
for i in 0..nr_actual_frames { sframes << tos2(csymbols[i]) }
|
||||||
|
for sframe in sframes {
|
||||||
|
executable := sframe.all_before('(')
|
||||||
|
addr := sframe.all_after('[').all_before(']')
|
||||||
|
cmd := 'addr2line -e $executable $addr'
|
||||||
|
|
||||||
|
// taken from os, to avoid depending on the os module inside builtin.v
|
||||||
|
f := C.popen(cmd.str, 'r')
|
||||||
|
if isnil(f) {
|
||||||
|
println(sframe) continue
|
||||||
|
}
|
||||||
|
buf := [1000]byte
|
||||||
|
mut output := ''
|
||||||
|
for C.fgets(voidptr(buf), 1000, f) != 0 {
|
||||||
|
output += tos(buf, vstrlen(buf))
|
||||||
|
}
|
||||||
|
output = output.trim_space()+':'
|
||||||
|
if 0 != int(C.pclose(f)) {
|
||||||
|
println(sframe) continue
|
||||||
|
}
|
||||||
|
println( '${output:-45s} | $sframe')
|
||||||
|
}
|
||||||
|
//C.backtrace_symbols_fd(*voidptr(&buffer[skipframes]), nr_actual_frames, 1)
|
||||||
|
return true
|
||||||
|
}$else{
|
||||||
|
C.printf('backtrace_symbols_fd is missing, so printing backtraces is not available.\n')
|
||||||
|
C.printf('Some libc implementations like musl simply do not provide it.\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
module builtin
|
||||||
|
|
||||||
|
#include <dbghelp.h>
|
||||||
|
#flag windows -l dbghelp
|
||||||
|
|
||||||
|
pub struct SymbolInfo {
|
||||||
|
pub mut:
|
||||||
|
f_size_of_struct u32 // must be 88 to be recognised by SymFromAddr
|
||||||
|
f_type_index u32 // Type Index of symbol
|
||||||
|
f_reserved [2]u64
|
||||||
|
f_index u32
|
||||||
|
f_size u32
|
||||||
|
f_mod_base u64 // Base Address of module comtaining this symbol
|
||||||
|
f_flags u32
|
||||||
|
f_value u64 // Value of symbol, ValuePresent should be 1
|
||||||
|
f_address u64 // Address of symbol including base address of module
|
||||||
|
f_register u32 // register holding value or pointer to value
|
||||||
|
f_scope u32 // scope of the symbol
|
||||||
|
f_tag u32 // pdb classification
|
||||||
|
f_name_len u32 // Actual length of name
|
||||||
|
f_max_name_len u32 // must be manually set
|
||||||
|
f_name byte // must be calloc(f_max_name_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SymbolInfoContainer {
|
||||||
|
pub mut:
|
||||||
|
syminfo SymbolInfo
|
||||||
|
f_name_rest [254]char
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Line64 {
|
||||||
|
f_size_of_struct u32
|
||||||
|
f_key voidptr
|
||||||
|
f_line_number u32
|
||||||
|
f_file_name byteptr
|
||||||
|
f_address u64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn C.SymSetOptions(symoptions u32) u32 // returns the current options mask
|
||||||
|
fn C.GetCurrentProcess() voidptr // returns handle
|
||||||
|
fn C.SymInitialize(h_process voidptr, p_user_search_path byteptr, b_invade_process int) int
|
||||||
|
fn C.CaptureStackBackTrace(frames_to_skip u32, frames_to_capture u32, p_backtrace voidptr, p_backtrace_hash voidptr) u16
|
||||||
|
fn C.SymFromAddr(h_process voidptr, address u64, p_displacement voidptr, p_symbol voidptr) int
|
||||||
|
fn C.SymGetLineFromAddr64(h_process voidptr, address u64, p_displacement voidptr, p_line &Line64) int
|
||||||
|
|
||||||
|
// Ref - https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions
|
||||||
|
const (
|
||||||
|
SYMOPT_UNDNAME = 0x00000002
|
||||||
|
SYMOPT_DEFERRED_LOADS = 0x00000004
|
||||||
|
SYMOPT_NO_CPP = 0x00000008
|
||||||
|
SYMOPT_LOAD_LINES = 0x00000010
|
||||||
|
SYMOPT_INCLUDE_32BIT_MODULES = 0x00002000
|
||||||
|
SYMOPT_ALLOW_ZERO_ADDRESS = 0x01000000
|
||||||
|
SYMOPT_DEBUG = 0x80000000
|
||||||
|
)
|
||||||
|
|
||||||
|
fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool {
|
||||||
|
mut offset := u64(0)
|
||||||
|
backtraces := [100]voidptr
|
||||||
|
sic := SymbolInfoContainer{}
|
||||||
|
mut si := &sic.syminfo
|
||||||
|
si.f_size_of_struct = sizeof(SymbolInfo) // Note: C.SYMBOL_INFO is 88
|
||||||
|
si.f_max_name_len = sizeof(SymbolInfoContainer) - sizeof(SymbolInfo) - 1
|
||||||
|
fname := *char( &si.f_name )
|
||||||
|
mut sline64 := Line64{}
|
||||||
|
sline64.f_size_of_struct = sizeof(Line64)
|
||||||
|
|
||||||
|
handle := C.GetCurrentProcess()
|
||||||
|
defer { C.SymCleanup(handle) }
|
||||||
|
|
||||||
|
options := C.SymSetOptions(SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME)
|
||||||
|
syminitok := C.SymInitialize( handle, 0, 1)
|
||||||
|
if syminitok != 1 {
|
||||||
|
println('Failed getting process: Aborting backtrace.\n')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
frames := int( C.CaptureStackBackTrace(skipframes + 1, 100, backtraces, 0) )
|
||||||
|
for i:=0; i < frames; i++ {
|
||||||
|
// fugly pointer arithmetics follows ...
|
||||||
|
s := *voidptr( u64(backtraces) + u64(i*sizeof(voidptr)) )
|
||||||
|
symfa_ok := C.SymFromAddr( handle, *s, &offset, si )
|
||||||
|
if symfa_ok == 1 {
|
||||||
|
nframe := frames - i - 1
|
||||||
|
mut lineinfo := ''
|
||||||
|
symglfa_ok := C.SymGetLineFromAddr64(handle, *s, &offset, &sline64)
|
||||||
|
if symglfa_ok == 1 {
|
||||||
|
lineinfo = ' ${sline64.f_file_name}:${sline64.f_line_number}'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//cerr := int(C.GetLastError()) println('SymGetLineFromAddr64 failure: $cerr ')
|
||||||
|
lineinfo = ' ?? : address= $s'
|
||||||
|
}
|
||||||
|
sfunc := tos3(fname)
|
||||||
|
println('${nframe:-2d}: ${sfunc:-25s} $lineinfo')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes
|
||||||
|
cerr := int(C.GetLastError())
|
||||||
|
if (cerr == 87) {
|
||||||
|
println('SymFromAddr failure: $cerr = The parameter is incorrect)')
|
||||||
|
}
|
||||||
|
else if (cerr == 487) {
|
||||||
|
// probably caused because the .pdb isn't in the executable folder
|
||||||
|
println('SymFromAddr failure: $cerr = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println('SymFromAddr failure: $cerr (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_backtrace_skipping_top_frames_mingw(skipframes int) bool {
|
||||||
|
println('TODO: print_backtrace_skipping_top_frames_mingw($skipframes)')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_backtrace_skipping_top_frames_nix(skipframes int) bool {
|
||||||
|
println('not implemented, see builtin_nix.v')
|
||||||
|
return false
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ CommonCHeaders = '
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h> // sleep
|
#include <unistd.h> // sleep
|
||||||
#else
|
#else
|
||||||
|
#pragma comment(lib, "Dbghelp.lib")
|
||||||
#if defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < __MSVCR90_DLL
|
#if defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < __MSVCR90_DLL
|
||||||
#error Please upgrade your MinGW distribution to use msvcr90.dll or later.
|
#error Please upgrade your MinGW distribution to use msvcr90.dll or later.
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,7 +3,7 @@ module compiler
|
||||||
import os
|
import os
|
||||||
|
|
||||||
#flag windows -l shell32
|
#flag windows -l shell32
|
||||||
|
#flag windows -l dbghelp
|
||||||
// RegOpenKeyExA etc
|
// RegOpenKeyExA etc
|
||||||
#flag windows -l advapi32
|
#flag windows -l advapi32
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
Test for backtrace capability
|
||||||
|
*/
|
||||||
|
fn a_method() {
|
||||||
|
print_backtrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_backtrace() {
|
||||||
|
a_method()
|
||||||
|
//panic('hi')
|
||||||
|
}
|
|
@ -86,7 +86,6 @@ fn init_os_args(argc int, argv &byteptr) []string {
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn ls(path string) ?[]string {
|
pub fn ls(path string) ?[]string {
|
||||||
mut find_file_data := Win32finddata{}
|
mut find_file_data := Win32finddata{}
|
||||||
mut dir_files := []string
|
mut dir_files := []string
|
||||||
|
|
Loading…
Reference in New Issue