mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Merge branch 'master' of https://github.com/odin-lang/Odin
This commit is contained in:
+29
-17
@@ -1,26 +1,38 @@
|
|||||||
/*
|
/*
|
||||||
import "core:prof/spall"
|
import "core:prof/spall"
|
||||||
|
|
||||||
spall_ctx: spall.Context
|
spall_ctx: spall.Context
|
||||||
spall_buffer: spall.Buffer
|
spall_buffer: spall.Buffer
|
||||||
|
|
||||||
foo :: proc() {
|
foo :: proc() {
|
||||||
spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
|
spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
spall_ctx = spall.context_create("trace_test.spall")
|
spall_ctx = spall.context_create("trace_test.spall")
|
||||||
defer spall.context_destroy(&spall_ctx)
|
defer spall.context_destroy(&spall_ctx)
|
||||||
|
|
||||||
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
|
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
|
||||||
spall_buffer = spall.buffer_create(buffer_backing)
|
spall_buffer = spall.buffer_create(buffer_backing)
|
||||||
defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
|
defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
|
||||||
|
|
||||||
spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
|
spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
|
||||||
|
|
||||||
for i := 0; i < 9001; i += 1 {
|
for i := 0; i < 9001; i += 1 {
|
||||||
foo()
|
foo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Automatic profiling of every procedure:
|
||||||
|
|
||||||
|
@(instrumentation_enter)
|
||||||
|
spall_enter :: proc "contextless" (proc_address, call_site_return_address: rawptr, loc: runtime.Source_Code_Location) {
|
||||||
|
spall._buffer_begin(&spall_ctx, &spall_buffer, "", "", loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(instrumentation_exit)
|
||||||
|
spall_exit :: proc "contextless" (proc_address, call_site_return_address: rawptr, loc: runtime.Source_Code_Location) {
|
||||||
|
spall._buffer_end(&spall_ctx, &spall_buffer)
|
||||||
|
}
|
||||||
*/
|
*/
|
||||||
package spall
|
package spall
|
||||||
|
|||||||
+25
-10
@@ -3,7 +3,6 @@ package spall
|
|||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
|
||||||
|
|
||||||
// File Format
|
// File Format
|
||||||
|
|
||||||
@@ -111,9 +110,10 @@ buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buff
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_flush :: proc(ctx: ^Context, buffer: ^Buffer) {
|
@(no_instrumentation)
|
||||||
|
buffer_flush :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||||
start := _trace_now(ctx)
|
start := _trace_now(ctx)
|
||||||
os.write(ctx.fd, buffer.data[:buffer.head])
|
write(ctx.fd, buffer.data[:buffer.head])
|
||||||
buffer.head = 0
|
buffer.head = 0
|
||||||
end := _trace_now(ctx)
|
end := _trace_now(ctx)
|
||||||
|
|
||||||
@@ -140,15 +140,16 @@ _scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #c
|
|||||||
_buffer_end(ctx, buffer)
|
_buffer_end(ctx, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
_trace_now :: proc "contextless" (ctx: ^Context) -> f64 {
|
_trace_now :: proc "contextless" (ctx: ^Context) -> f64 {
|
||||||
if !ctx.precise_time {
|
if !ctx.precise_time {
|
||||||
return f64(time.tick_now()._nsec) / 1_000
|
return f64(tick_now()) / 1_000
|
||||||
}
|
}
|
||||||
|
|
||||||
return f64(intrinsics.read_cycle_counter())
|
return f64(intrinsics.read_cycle_counter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok {
|
_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok {
|
||||||
header_size = size_of(Manual_Header)
|
header_size = size_of(Manual_Header)
|
||||||
if header_size > len(buffer) {
|
if header_size > len(buffer) {
|
||||||
@@ -164,7 +165,8 @@ _build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (hea
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_build_begin :: proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
|
@(no_instrumentation)
|
||||||
|
_build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||||
ev := (^Begin_Event)(raw_data(buffer))
|
ev := (^Begin_Event)(raw_data(buffer))
|
||||||
name_len := min(len(name), 255)
|
name_len := min(len(name), 255)
|
||||||
args_len := min(len(args), 255)
|
args_len := min(len(args), 255)
|
||||||
@@ -180,13 +182,14 @@ _build_begin :: proc "contextless" (buffer: []u8, name: string, args: string, ts
|
|||||||
ev.ts = f64le(ts)
|
ev.ts = f64le(ts)
|
||||||
ev.name_len = u8(name_len)
|
ev.name_len = u8(name_len)
|
||||||
ev.args_len = u8(args_len)
|
ev.args_len = u8(args_len)
|
||||||
mem.copy(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len)
|
intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len)
|
||||||
mem.copy(raw_data(buffer[size_of(Begin_Event)+name_len:]), raw_data(args), args_len)
|
intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Begin_Event)+name_len:]), raw_data(args), args_len)
|
||||||
ok = true
|
ok = true
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
_build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
|
_build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
|
||||||
ev := (^End_Event)(raw_data(buffer))
|
ev := (^End_Event)(raw_data(buffer))
|
||||||
event_size = size_of(End_Event)
|
event_size = size_of(End_Event)
|
||||||
@@ -203,7 +206,8 @@ _build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) ->
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer_begin :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) {
|
@(no_instrumentation)
|
||||||
|
_buffer_begin :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||||
if buffer.head + BEGIN_EVENT_MAX > len(buffer.data) {
|
if buffer.head + BEGIN_EVENT_MAX > len(buffer.data) {
|
||||||
buffer_flush(ctx, buffer)
|
buffer_flush(ctx, buffer)
|
||||||
}
|
}
|
||||||
@@ -211,7 +215,8 @@ _buffer_begin :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string
|
|||||||
buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx), buffer.tid, buffer.pid)
|
buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx), buffer.tid, buffer.pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer) {
|
@(no_instrumentation)
|
||||||
|
_buffer_end :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||||
ts := _trace_now(ctx)
|
ts := _trace_now(ctx)
|
||||||
|
|
||||||
if buffer.head + size_of(End_Event) > len(buffer.data) {
|
if buffer.head + size_of(End_Event) > len(buffer.data) {
|
||||||
@@ -220,3 +225,13 @@ _buffer_end :: proc(ctx: ^Context, buffer: ^Buffer) {
|
|||||||
|
|
||||||
buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid)
|
buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
write :: proc "contextless" (fd: os.Handle, buf: []byte) -> (n: int, err: os.Errno) {
|
||||||
|
return _write(fd, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
tick_now :: proc "contextless" () -> (ns: i64) {
|
||||||
|
return _tick_now()
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
//+private
|
||||||
|
package spall
|
||||||
|
|
||||||
|
// Only for types and constants.
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
// Package is `//+no-instrumentation`, safe to use.
|
||||||
|
import "core:sys/linux"
|
||||||
|
|
||||||
|
MAX_RW :: 0x7fffffff
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, os.ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
for n < len(data) {
|
||||||
|
chunk := data[:min(len(data), MAX_RW)]
|
||||||
|
written, errno := linux.write(linux.Fd(fd), chunk)
|
||||||
|
if errno != .NONE {
|
||||||
|
return n, os.Errno(errno)
|
||||||
|
}
|
||||||
|
n += written
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, os.ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
_tick_now :: proc "contextless" () -> (ns: i64) {
|
||||||
|
t, _ := linux.clock_gettime(.MONOTONIC_RAW)
|
||||||
|
return i64(t.time_sec)*1e9 + i64(t.time_nsec)
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
//+private
|
||||||
|
//+build darwin, freebsd, openbsd
|
||||||
|
package spall
|
||||||
|
|
||||||
|
// Only for types.
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
when ODIN_OS == .Darwin {
|
||||||
|
foreign import libc "system:System.framework"
|
||||||
|
} else {
|
||||||
|
foreign import libc "system:c"
|
||||||
|
}
|
||||||
|
|
||||||
|
timespec :: struct {
|
||||||
|
tv_sec: i64, // seconds
|
||||||
|
tv_nsec: i64, // nanoseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
__error :: proc() -> ^i32 ---
|
||||||
|
@(link_name="write") _unix_write :: proc(handle: os.Handle, buffer: rawptr, count: uint) -> int ---
|
||||||
|
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
get_last_error :: proc "contextless" () -> int {
|
||||||
|
return int(__error()^)
|
||||||
|
}
|
||||||
|
|
||||||
|
MAX_RW :: 0x7fffffff
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, os.ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
for n < len(data) {
|
||||||
|
chunk := data[:min(len(data), MAX_RW)]
|
||||||
|
written := _unix_write(fd, raw_data(chunk), len(chunk))
|
||||||
|
if written < 0 {
|
||||||
|
return n, os.Errno(get_last_error())
|
||||||
|
}
|
||||||
|
n += written
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, os.ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
_tick_now :: proc "contextless" () -> (ns: i64) {
|
||||||
|
t: timespec
|
||||||
|
_unix_clock_gettime(CLOCK_MONOTONIC_RAW, &t)
|
||||||
|
return t.tv_sec*1e9 + t.tv_nsec
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
//+private
|
||||||
|
package spall
|
||||||
|
|
||||||
|
// Only for types.
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
// Package is `//+no-instrumentation`, safe to use.
|
||||||
|
import win32 "core:sys/windows"
|
||||||
|
|
||||||
|
MAX_RW :: 1<<30
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (int, os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, os.ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
single_write_length: win32.DWORD
|
||||||
|
total_write: i64
|
||||||
|
length := i64(len(data))
|
||||||
|
|
||||||
|
for total_write < length {
|
||||||
|
remaining := length - total_write
|
||||||
|
to_write := win32.DWORD(min(i32(remaining), MAX_RW))
|
||||||
|
|
||||||
|
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
|
||||||
|
if single_write_length <= 0 || !e {
|
||||||
|
err := os.Errno(win32.GetLastError())
|
||||||
|
return int(total_write), err
|
||||||
|
}
|
||||||
|
total_write += i64(single_write_length)
|
||||||
|
}
|
||||||
|
return int(total_write), os.ERROR_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
|
_tick_now :: proc "contextless" () -> (ns: i64) {
|
||||||
|
@(no_instrumentation)
|
||||||
|
mul_div_u64 :: #force_inline proc "contextless" (val, num, den: i64) -> i64 {
|
||||||
|
q := val / den
|
||||||
|
r := val % den
|
||||||
|
return q * num + r * num / den
|
||||||
|
}
|
||||||
|
|
||||||
|
@thread_local qpc_frequency: win32.LARGE_INTEGER
|
||||||
|
|
||||||
|
if qpc_frequency == 0 {
|
||||||
|
win32.QueryPerformanceFrequency(&qpc_frequency)
|
||||||
|
}
|
||||||
|
now: win32.LARGE_INTEGER
|
||||||
|
win32.QueryPerformanceCounter(&now)
|
||||||
|
|
||||||
|
return mul_div_u64(i64(now), 1e9, i64(qpc_frequency))
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//+build linux
|
//+build linux
|
||||||
|
//+no-instrumentation
|
||||||
package linux
|
package linux
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//+no-instrumentation
|
||||||
package linux
|
package linux
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
@@ -2383,7 +2384,11 @@ timer_delete :: proc "contextless" (timer: Timer) -> (Errno) {
|
|||||||
|
|
||||||
// TODO(flysand): clock_settime
|
// TODO(flysand): clock_settime
|
||||||
|
|
||||||
// TODO(flysand): clock_gettime
|
clock_gettime :: proc "contextless" (clock: Clock_Id) -> (ts: Time_Spec, err: Errno) {
|
||||||
|
ret := syscall(SYS_clock_gettime, clock, &ts)
|
||||||
|
err = Errno(-ret)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(flysand): clock_getres
|
// TODO(flysand): clock_getres
|
||||||
|
|
||||||
|
|||||||
Vendored
-2
@@ -120,7 +120,6 @@ when GL_DEBUG {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compiling shaders are identical for any shader (vertex, geometry, fragment, tesselation, (maybe compute too))
|
// Compiling shaders are identical for any shader (vertex, geometry, fragment, tesselation, (maybe compute too))
|
||||||
@private
|
|
||||||
compile_shader_from_source :: proc(shader_data: string, shader_type: Shader_Type) -> (shader_id: u32, ok: bool) {
|
compile_shader_from_source :: proc(shader_data: string, shader_type: Shader_Type) -> (shader_id: u32, ok: bool) {
|
||||||
shader_id = CreateShader(cast(u32)shader_type)
|
shader_id = CreateShader(cast(u32)shader_type)
|
||||||
length := i32(len(shader_data))
|
length := i32(len(shader_data))
|
||||||
@@ -134,7 +133,6 @@ compile_shader_from_source :: proc(shader_data: string, shader_type: Shader_Type
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only used once, but I'd just make a subprocedure(?) for consistency
|
// only used once, but I'd just make a subprocedure(?) for consistency
|
||||||
@private
|
|
||||||
create_and_link_program :: proc(shader_ids: []u32, binary_retrievable := false) -> (program_id: u32, ok: bool) {
|
create_and_link_program :: proc(shader_ids: []u32, binary_retrievable := false) -> (program_id: u32, ok: bool) {
|
||||||
program_id = CreateProgram()
|
program_id = CreateProgram()
|
||||||
for id in shader_ids {
|
for id in shader_ids {
|
||||||
|
|||||||
Reference in New Issue
Block a user