mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-02 02:03:35 +00:00
[core:debug/trace] remove os, fmt, slice, and strconv imports (#7408)
* [core:debug/trace] remove `os`, `fmt`, `slice`, and `strconv` imports * [core:debug/trace]: print memory nicely A bit of a modified, minimal core:fmt `%m` (to not need `core:strconv`)
This commit is contained in:
@@ -3,7 +3,6 @@ package debug_trace
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:sync"
|
||||
|
||||
@@ -290,43 +289,49 @@ tracking_allocator_print_results :: proc(t: ^Tracking_Allocator, temp_allocator
|
||||
ALLOCATOR_MAX_BACKTRACES :: 16
|
||||
|
||||
for _, leak in t.allocation_map {
|
||||
fmt.eprintfln("%v leaked %m", leak.location, leak.size)
|
||||
runtime.print_caller_location(leak.location)
|
||||
runtime.print_string(" leaked ")
|
||||
_print_memory(leak.size)
|
||||
runtime.print_byte('\n')
|
||||
|
||||
defer i += 1
|
||||
if i > ALLOCATOR_MAX_BACKTRACES {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.eprintln("[back trace]")
|
||||
runtime.print_string("[back trace]\n")
|
||||
|
||||
trace, err := resolve(leak.backtrace, temp_allocator, temp_allocator)
|
||||
if err != nil {
|
||||
fmt.eprintfln("\tbacktrace error: %v", err)
|
||||
runtime.print_string("\tbacktrace error: ")
|
||||
runtime.print_string(resolve_err_string(err))
|
||||
runtime.print_string("\n")
|
||||
continue
|
||||
}
|
||||
defer locations_destroy(trace, temp_allocator)
|
||||
|
||||
print(trace)
|
||||
fmt.eprintln()
|
||||
runtime.print_string("\n")
|
||||
}
|
||||
|
||||
for bad_free, _ in t.bad_free_array {
|
||||
fmt.eprintfln(
|
||||
"%v allocation %p was freed badly",
|
||||
bad_free.location,
|
||||
bad_free.memory,
|
||||
)
|
||||
runtime.print_caller_location(bad_free.location)
|
||||
runtime.print_string(" allocation ")
|
||||
runtime.print_u64(u64(uintptr(bad_free.memory)))
|
||||
runtime.print_string(" was freed badly\n")
|
||||
|
||||
defer i += 1
|
||||
if i > ALLOCATOR_MAX_BACKTRACES {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.eprintln("[back trace]")
|
||||
runtime.print_string("[back trace]\n")
|
||||
|
||||
trace, err := resolve(bad_free.backtrace, temp_allocator, temp_allocator)
|
||||
if err != nil {
|
||||
fmt.eprintfln("\tbacktrace error: %v", err)
|
||||
runtime.print_string("\tbacktrace error: ")
|
||||
runtime.print_string(resolve_err_string(err))
|
||||
runtime.print_string("\n")
|
||||
continue
|
||||
}
|
||||
defer locations_destroy(trace, temp_allocator)
|
||||
@@ -334,3 +339,37 @@ tracking_allocator_print_results :: proc(t: ^Tracking_Allocator, temp_allocator
|
||||
print(trace)
|
||||
}
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
_MEMORY_UNITS := [?]string{"b", "kib", "mib", "gib", "tib", "pib", "eib"}
|
||||
|
||||
_print_memory :: proc(size: int) {
|
||||
assert(size >= 0)
|
||||
|
||||
u := u64(size)
|
||||
unit_idx := 0
|
||||
div: u64 = 1
|
||||
for u / div >= mem.Kilobyte && unit_idx < len(_MEMORY_UNITS) - 1 {
|
||||
div *= mem.Kilobyte
|
||||
unit_idx += 1
|
||||
}
|
||||
|
||||
whole := u / div
|
||||
rem := u % div
|
||||
frac := (rem * 10 + div / 2) / div
|
||||
if frac == 10 {
|
||||
frac = 0
|
||||
whole += 1
|
||||
if whole == mem.Kilobyte && unit_idx < len(_MEMORY_UNITS) - 1 {
|
||||
whole = 1
|
||||
unit_idx += 1
|
||||
}
|
||||
}
|
||||
|
||||
runtime.print_u64(whole)
|
||||
if frac != 0 {
|
||||
runtime.print_byte('.')
|
||||
runtime.print_byte(byte('0' + frac))
|
||||
}
|
||||
runtime.print_string(_MEMORY_UNITS[unit_idx])
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package debug_trace
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:fmt"
|
||||
import "core:sync"
|
||||
|
||||
// Size of a constant backtrace, as used by the tracking allocator for example.
|
||||
@@ -87,6 +86,16 @@ Resolve_Error :: enum {
|
||||
Resolve_Aborted,
|
||||
}
|
||||
|
||||
resolve_err_string :: proc(err: Resolve_Error) -> string {
|
||||
switch err {
|
||||
case .None: return "none"
|
||||
case .Allocator_Error: return "allocator error"
|
||||
case .Parse_Address_Failed: return "parse address failed"
|
||||
case .Resolve_Aborted: return "resolve aborted"
|
||||
case: return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
Location :: runtime.Source_Code_Location
|
||||
|
||||
/*
|
||||
@@ -133,9 +142,11 @@ assertion_failure_proc :: proc(prefix, message: string, loc: runtime.Source_Code
|
||||
|
||||
lines, err := resolve(capture(skip=1), context.temp_allocator, context.temp_allocator)
|
||||
if err != nil {
|
||||
fmt.eprintfln("could not get backtrace for assertion failure: %v", err)
|
||||
runtime.print_string("could not get backtrace for assertion failure: ")
|
||||
runtime.print_string(resolve_err_string(err))
|
||||
runtime.print_string("\n")
|
||||
} else {
|
||||
fmt.eprintfln("[back trace]")
|
||||
runtime.print_string("[back trace]\n")
|
||||
print(lines)
|
||||
locations_destroy(lines, context.temp_allocator)
|
||||
}
|
||||
@@ -153,26 +164,35 @@ Inputs:
|
||||
*/
|
||||
print :: proc(locations: []Location, padding := "\t") {
|
||||
for location, i in locations {
|
||||
fmt.eprintf("%s#%v %v at %v", padding, i, location.procedure, location.file_path)
|
||||
runtime.print_string(padding)
|
||||
runtime.print_string("#")
|
||||
runtime.print_int(i)
|
||||
runtime.print_string(" ")
|
||||
runtime.print_string(location.procedure)
|
||||
runtime.print_string(" at ")
|
||||
runtime.print_string(location.file_path)
|
||||
if location.line > 0 {
|
||||
when ODIN_ERROR_POS_STYLE == .Default {
|
||||
fmt.eprintf("(%v", location.line)
|
||||
runtime.print_string("(")
|
||||
runtime.print_i64(i64(location.line))
|
||||
if location.column > 0 {
|
||||
fmt.eprintf(":%v)", location.column)
|
||||
} else {
|
||||
fmt.eprint(")")
|
||||
runtime.print_string(":")
|
||||
runtime.print_i64(i64(location.column))
|
||||
}
|
||||
runtime.print_string(")")
|
||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||
fmt.eprintf(":%v", location.line)
|
||||
runtime.print_string(":")
|
||||
runtime.print_i64(i64(location.line))
|
||||
if location.column > 0 {
|
||||
fmt.eprintf(":%v", location.column)
|
||||
runtime.print_string(":")
|
||||
runtime.print_i64(i64(location.column))
|
||||
}
|
||||
} else {
|
||||
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.eprintln()
|
||||
runtime.print_string("\n")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ package debug_trace
|
||||
|
||||
@require import "base:runtime"
|
||||
|
||||
@require import "core:slice"
|
||||
|
||||
when INSTRUMENTATION_MODE {
|
||||
|
||||
when ODIN_OPTIMIZATION_MODE == .None {
|
||||
@@ -55,8 +53,11 @@ _locations_destroy :: proc(locations: []Location, allocator: runtime.Allocator)
|
||||
|
||||
@(private="package")
|
||||
_resolve :: proc(bt: Capture, allocator, temp_allocator: runtime.Allocator) -> (out: []Location, err: Resolve_Error) {
|
||||
clone, mem_err := slice.clone(bt, allocator)
|
||||
clone, mem_err := make(Capture, len(bt), allocator)
|
||||
if mem_err != nil { return nil, .Allocator_Error }
|
||||
for entry, i in bt {
|
||||
clone[i] = entry
|
||||
}
|
||||
return transmute([]Location)clone, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,8 @@ package debug_trace
|
||||
@require import "base:runtime"
|
||||
|
||||
@require import "core:c"
|
||||
@require import "core:os"
|
||||
@require import "core:strconv"
|
||||
@require import "core:strings"
|
||||
@require import "core:sys/posix"
|
||||
|
||||
when !INSTRUMENTATION_MODE {
|
||||
|
||||
@@ -153,19 +152,18 @@ _resolve :: proc(bt: Capture, allocator, temp_allocator: runtime.Allocator) -> (
|
||||
}
|
||||
|
||||
exec_and_fill :: proc(command: []string, locations: []Location, msgs: []cstring, allocator, temp_allocator: runtime.Allocator) -> (filled: int, err: Resolve_Error) {
|
||||
state, stdout, stderr, perr := os.process_exec({command = command}, temp_allocator)
|
||||
stdout, exec_errno, success := exec_symbolizer(command, temp_allocator)
|
||||
defer delete(stdout, temp_allocator)
|
||||
defer delete(stderr, temp_allocator)
|
||||
|
||||
count := len(command)-COMMAND_START_LEN
|
||||
|
||||
// `SYMBOLIZER_PROGRAM` does not exist, lets fall back to unresolved info.
|
||||
if perr == .Not_Exist {
|
||||
if exec_errno == .ENOENT {
|
||||
fill_unresolved(locations, msgs, allocator)
|
||||
return count, nil
|
||||
}
|
||||
|
||||
if perr != nil || !state.success {
|
||||
if !success {
|
||||
return 0, .Resolve_Aborted
|
||||
}
|
||||
|
||||
@@ -201,18 +199,156 @@ _resolve :: proc(bt: Capture, allocator, temp_allocator: runtime.Allocator) -> (
|
||||
colon_idx := strings.last_index_byte(location, ':')
|
||||
if colon_idx > 0 {
|
||||
line_str := location[colon_idx+1:]
|
||||
if line_int, ok := strconv.parse_i64_of_base(line_str, 10); ok {
|
||||
if line_int, ok := parse_line_number(line_str); ok {
|
||||
file_path = location[:colon_idx]
|
||||
line = i32(line_int)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
parse_line_number :: proc(s: string) -> (n: i64, ok: bool) {
|
||||
if len(s) == 0 { return }
|
||||
for c in s {
|
||||
if c < '0' || c > '9' { return }
|
||||
n = n*10 + i64(c-'0')
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
}
|
||||
|
||||
process_line :: proc(line: string, ok: bool) -> (string, Resolve_Error) {
|
||||
if !ok || line == "" { return "", .Resolve_Aborted }
|
||||
return strings.trim_right_space(line), nil
|
||||
}
|
||||
|
||||
exec_symbolizer :: proc(command: []string, allocator: runtime.Allocator) -> (stdout: []byte, exec_errno: posix.Errno, ok: bool) {
|
||||
cargs, cargs_err := make([]cstring, len(command)+1, allocator)
|
||||
if cargs_err != nil {
|
||||
return
|
||||
}
|
||||
defer delete(cargs, allocator)
|
||||
|
||||
args_size := 0
|
||||
for arg in command {
|
||||
args_size += len(arg)+1
|
||||
}
|
||||
args, args_err := make([]byte, args_size, allocator)
|
||||
if args_err != nil {
|
||||
return
|
||||
}
|
||||
defer delete(args, allocator)
|
||||
|
||||
offset := 0
|
||||
for arg, i in command {
|
||||
cargs[i] = cstring(&args[offset])
|
||||
copy(args[offset:], arg)
|
||||
offset += len(arg)+1
|
||||
}
|
||||
|
||||
READ, WRITE :: 0, 1
|
||||
stdout_pipe, exec_pipe: [2]posix.FD
|
||||
if posix.pipe(&stdout_pipe) != .OK {
|
||||
return
|
||||
}
|
||||
if posix.pipe(&exec_pipe) != .OK {
|
||||
posix.close(stdout_pipe[READ])
|
||||
posix.close(stdout_pipe[WRITE])
|
||||
return
|
||||
}
|
||||
if posix.fcntl(exec_pipe[WRITE], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
|
||||
posix.close(stdout_pipe[READ])
|
||||
posix.close(stdout_pipe[WRITE])
|
||||
posix.close(exec_pipe[READ])
|
||||
posix.close(exec_pipe[WRITE])
|
||||
return
|
||||
}
|
||||
|
||||
pid := posix.fork()
|
||||
if pid == -1 {
|
||||
posix.close(stdout_pipe[READ])
|
||||
posix.close(stdout_pipe[WRITE])
|
||||
posix.close(exec_pipe[READ])
|
||||
posix.close(exec_pipe[WRITE])
|
||||
return
|
||||
}
|
||||
|
||||
if pid == 0 {
|
||||
abort :: proc(exec_fd: posix.FD) -> ! {
|
||||
errno := posix.errno()
|
||||
posix.write(exec_fd, ([^]byte)(&errno), size_of(errno))
|
||||
posix._exit(126)
|
||||
}
|
||||
|
||||
posix.close(stdout_pipe[READ])
|
||||
posix.close(exec_pipe[READ])
|
||||
if posix.dup2(stdout_pipe[WRITE], posix.STDOUT_FILENO) == -1 {
|
||||
abort(exec_pipe[WRITE])
|
||||
}
|
||||
|
||||
dev_null := posix.open("/dev/null", {.WRONLY})
|
||||
if dev_null == -1 || posix.dup2(dev_null, posix.STDERR_FILENO) == -1 {
|
||||
abort(exec_pipe[WRITE])
|
||||
}
|
||||
if dev_null != posix.STDERR_FILENO {
|
||||
posix.close(dev_null)
|
||||
}
|
||||
if stdout_pipe[WRITE] != posix.STDOUT_FILENO {
|
||||
posix.close(stdout_pipe[WRITE])
|
||||
}
|
||||
|
||||
posix.execvp(cargs[0], raw_data(cargs))
|
||||
abort(exec_pipe[WRITE])
|
||||
}
|
||||
|
||||
posix.close(stdout_pipe[WRITE])
|
||||
posix.close(exec_pipe[WRITE])
|
||||
|
||||
output: [dynamic]byte
|
||||
output.allocator = allocator
|
||||
read_ok := true
|
||||
buf: [1024]byte
|
||||
for {
|
||||
n := posix.read(stdout_pipe[READ], raw_data(buf[:]), len(buf))
|
||||
if n > 0 {
|
||||
if _, err := append(&output, ..buf[:n]); err != nil {
|
||||
read_ok = false
|
||||
}
|
||||
} else if n == -1 && posix.errno() == .EINTR {
|
||||
continue
|
||||
} else {
|
||||
read_ok = read_ok && n == 0
|
||||
break
|
||||
}
|
||||
}
|
||||
posix.close(stdout_pipe[READ])
|
||||
|
||||
exec_errno = .NONE
|
||||
for {
|
||||
n := posix.read(exec_pipe[READ], ([^]byte)(&exec_errno), size_of(exec_errno))
|
||||
if n == -1 && posix.errno() == .EINTR {
|
||||
continue
|
||||
}
|
||||
read_ok = read_ok && (n == 0 || n == size_of(exec_errno))
|
||||
break
|
||||
}
|
||||
posix.close(exec_pipe[READ])
|
||||
|
||||
status: c.int
|
||||
for {
|
||||
if posix.waitpid(pid, &status, {}) == -1 {
|
||||
if posix.errno() == .EINTR {
|
||||
continue
|
||||
}
|
||||
read_ok = false
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
stdout = output[:]
|
||||
ok = read_ok && exec_errno == nil && posix.WIFEXITED(status) && posix.WEXITSTATUS(status) == 0
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
} // INSTRUMENTATION_MODE
|
||||
|
||||
Reference in New Issue
Block a user