This commit is contained in:
avanspector
2025-01-10 06:42:42 +01:00
12 changed files with 296 additions and 34 deletions
+41 -9
View File
@@ -34,12 +34,18 @@ Tracking_Allocator_Bad_Free_Entry :: struct {
location: runtime.Source_Code_Location,
}
/*
Callback type for when tracking allocator runs into a bad free.
*/
Tracking_Allocator_Bad_Free_Callback :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location)
/*
Tracking allocator data.
*/
Tracking_Allocator :: struct {
backing: Allocator,
allocation_map: map[rawptr]Tracking_Allocator_Entry,
bad_free_callback: Tracking_Allocator_Bad_Free_Callback,
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
mutex: sync.Mutex,
clear_on_free_all: bool,
@@ -61,6 +67,7 @@ allocate the tracked data.
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
t.backing = backing_allocator
t.allocation_map.allocator = internals_allocator
t.bad_free_callback = tracking_allocator_bad_free_callback_panic
t.bad_free_array.allocator = internals_allocator
if .Free_All in query_features(t.backing) {
t.clear_on_free_all = true
@@ -109,6 +116,33 @@ tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
sync.mutex_unlock(&t.mutex)
}
/*
Default behavior for a bad free: Crash with error message that says where the
bad free happened.
Override Tracking_Allocator.bad_free_callback to have something else happen. For
example, you can use tracking_allocator_bad_free_callback_add_to_array to return
the tracking allocator to the old behavior, where the bad_free_array was used.
*/
tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
runtime.print_caller_location(location)
runtime.print_string(" Tracking allocator error: Bad free of pointer ")
runtime.print_uintptr(uintptr(memory))
runtime.print_string("\n")
runtime.trap()
}
/*
Alternative behavior for a bad free: Store in `bad_free_array`. If you use this,
then you must make sure to check Tracking_Allocator.bad_free_array at some point.
*/
tracking_allocator_bad_free_callback_add_to_array :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
append(&t.bad_free_array, Tracking_Allocator_Bad_Free_Entry {
memory = memory,
location = location,
})
}
/*
Tracking allocator.
@@ -116,8 +150,10 @@ The tracking allocator is an allocator wrapper that tracks memory allocations.
This allocator stores all the allocations in a map. Whenever a pointer that's
not inside of the map is freed, the `bad_free_array` entry is added.
An example of how to use the `Tracking_Allocator` to track subsequent allocations
in your program and report leaks and bad frees:
Here follows an example of how to use the `Tracking_Allocator` to track
subsequent allocations in your program and report leaks. By default, the
tracking allocator will crash on bad frees. You can override that behavior by
overriding `track.bad_free_callback`.
Example:
@@ -137,9 +173,6 @@ Example:
for _, leak in track.allocation_map {
fmt.printf("%v leaked %m\n", leak.location, leak.size)
}
for bad_free in track.bad_free_array {
fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
}
}
*/
@(require_results)
@@ -191,10 +224,9 @@ tracking_allocator_proc :: proc(
}
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
memory = old_memory,
location = loc,
})
if data.bad_free_callback != nil {
data.bad_free_callback(data, old_memory, loc)
}
} else {
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
}
+11 -5
View File
@@ -89,6 +89,7 @@ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runt
entry := _intern_get_entry(m, text) or_return
return cstring(&entry.str[0]), nil
}
/*
Internal function to lookup whether the text string exists in the map, returns the entry
Sets and allocates the entry if it wasn't set yet
@@ -104,13 +105,15 @@ Returns:
- err: An allocator error if one occured, `nil` otherwise
*/
_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
if prev, ok := m.entries[text]; ok {
return prev, nil
}
if m.allocator.procedure == nil {
m.allocator = context.allocator
}
key_ptr, val_ptr, inserted := map_entry(&m.entries, text) or_return
if !inserted {
return val_ptr^, nil
}
entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
new_entry = (^Intern_Entry)(raw_data(bytes))
@@ -120,6 +123,9 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry
new_entry.str[new_entry.len] = 0
key := string(new_entry.str[:new_entry.len])
m.entries[key] = new_entry
return new_entry, nil
key_ptr^ = key
val_ptr^ = new_entry
return
}
+4 -1
View File
@@ -110,7 +110,10 @@ class WasmMemoryInterface {
}
loadCstring(ptr) {
const start = this.loadPtr(ptr);
return this.loadCstringDirect(this.loadPtr(ptr));
}
loadCstringDirect(start) {
if (start == 0) {
return null;
}
+1
View File
@@ -6,4 +6,5 @@ foreign import "system:Comctl32.lib"
@(default_calling_convention="system")
foreign Comctl32 {
LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
SetWindowSubclass :: proc(hwnd: HWND, pfnSubclass: SUBCLASSPROC, uIdSubclass: UINT_PTR, dwRefData: DWORD_PTR) ---
}
+1 -1
View File
@@ -15,7 +15,7 @@ MINIDUMP_DIRECTORY :: struct {
Location: MINIDUMP_LOCATION_DESCRIPTOR,
}
MINIDUMP_EXCEPTION_INFORMATION :: struct {
MINIDUMP_EXCEPTION_INFORMATION :: struct #max_field_align(4) {
ThreadId: DWORD,
ExceptionPointers: ^EXCEPTION_POINTERS,
ClientPointers: BOOL,
+2
View File
@@ -796,6 +796,8 @@ TIMERPROC :: #type proc "system" (HWND, UINT, UINT_PTR, DWORD)
WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
SUBCLASSPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR) -> LRESULT
HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
WINEVENTPROC :: #type proc "system" (
+4 -1
View File
@@ -2,6 +2,7 @@
package sys_windows
import "base:intrinsics"
import "core:c"
foreign import user32 "system:User32.lib"
@(default_calling_convention="system")
@@ -32,6 +33,8 @@ foreign user32 {
RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
RegisterHotKey :: proc(hnwd: HWND, id: c.int, fsModifiers: UINT, vk: UINT) -> BOOL ---
CreateWindowExW :: proc(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
@@ -553,7 +556,7 @@ MOUSE_ATTRIBUTES_CHANGED :: 0x04
MOUSE_MOVE_NOCOALESCE :: 0x08
RI_MOUSE_BUTTON_1_DOWN :: 0x0001
RI_MOUSE_LEFT_BUTTON_DOWNS :: RI_MOUSE_BUTTON_1_DOWN
RI_MOUSE_LEFT_BUTTON_DOWN :: RI_MOUSE_BUTTON_1_DOWN
RI_MOUSE_BUTTON_1_UP :: 0x0002
RI_MOUSE_LEFT_BUTTON_UP :: RI_MOUSE_BUTTON_1_UP
RI_MOUSE_BUTTON_2_DOWN :: 0x0004
+210
View File
@@ -0,0 +1,210 @@
#+build windows
package sys_windows
foreign import "system:xinput.lib"
// Device types available in XINPUT_CAPABILITIES
// Correspond to XINPUT_DEVTYPE_...
XINPUT_DEVTYPE :: enum BYTE {
GAMEPAD = 0x01,
}
// Device subtypes available in XINPUT_CAPABILITIES
// Correspond to XINPUT_DEVSUBTYPE_...
XINPUT_DEVSUBTYPE :: enum BYTE {
UNKNOWN = 0x00,
GAMEPAD = 0x01,
WHEEL = 0x02,
ARCADE_STICK = 0x03,
FLIGHT_STICK = 0x04,
DANCE_PAD = 0x05,
GUITAR = 0x06,
GUITAR_ALTERNATE = 0x07,
DRUM_KIT = 0x08,
GUITAR_BASS = 0x0B,
ARCADE_PAD = 0x13,
}
// Flags for XINPUT_CAPABILITIES
// Correspond to log2(XINPUT_CAPS_...)
XINPUT_CAP :: enum WORD {
FFB_SUPPORTED = 0,
WIRELESS = 1,
VOICE_SUPPORTED = 2,
PMD_SUPPORTED = 3,
NO_NAVIGATION = 4,
}
XINPUT_CAPS :: distinct bit_set[XINPUT_CAP;WORD]
// Constants for gamepad buttons
// Correspond to log2(XINPUT_GAMEPAD_...)
XINPUT_GAMEPAD_BUTTON_BIT :: enum WORD {
DPAD_UP = 0,
DPAD_DOWN = 1,
DPAD_LEFT = 2,
DPAD_RIGHT = 3,
START = 4,
BACK = 5,
LEFT_THUMB = 6,
RIGHT_THUMB = 7,
LEFT_SHOULDER = 8,
RIGHT_SHOULDER = 9,
A = 12,
B = 13,
X = 14,
Y = 15,
}
XINPUT_GAMEPAD_BUTTON :: distinct bit_set[XINPUT_GAMEPAD_BUTTON_BIT;WORD]
// Gamepad thresholds
XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE: SHORT : 7849
XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE: SHORT : 8689
XINPUT_GAMEPAD_TRIGGER_THRESHOLD: SHORT : 30
// Flags to pass to XInputGetCapabilities
// Corresponds to log2(XINPUT_FLAG_...)
XINPUT_FLAG_BIT :: enum WORD {
GAMEPAD = 0,
}
XINPUT_FLAG :: distinct bit_set[XINPUT_FLAG_BIT;DWORD]
// Devices that support batteries
// Corresponds to BATTERY_DEVTYPE_...
BATTERY_DEVTYPE :: enum BYTE {
GAMEPAD = 0x00,
HEADSET = 0x01,
}
// Flags for battery status level
// Correspond to BATTERY_TYPE_...
BATTERY_TYPE :: enum BYTE {
DISCONNECTED = 0x00, // This device is not connected
WIRED = 0x01, // Wired device, no battery
ALKALINE = 0x02, // Alkaline battery source
NIMH = 0x03, // Nickel Metal Hydride battery source
UNKNOWN = 0xFF, // Cannot determine the battery type
}
// These are only valid for wireless, connected devices, with known battery types
// The amount of use time remaining depends on the type of device.
// Correspond to BATTERY_LEVEL_...
BATTERY_LEVEL :: enum BYTE {
EMPTY = 0x00,
LOW = 0x01,
MEDIUM = 0x02,
FULL = 0x03,
}
// User index definitions
// Index of the gamer associated with the device
XUSER :: enum DWORD {
One = 0,
Two = 1,
Three = 2,
Four = 3,
Any = 0x000000FF, // Can be only used with XInputGetKeystroke
}
XUSER_MAX_COUNT :: 4
// Codes returned for the gamepad keystroke
// Corresponds to VK_PAD_...
VK_PAD :: enum WORD {
A = 0x5800,
B = 0x5801,
X = 0x5802,
Y = 0x5803,
RSHOULDER = 0x5804,
LSHOULDER = 0x5805,
LTRIGGER = 0x5806,
RTRIGGER = 0x5807,
DPAD_UP = 0x5810,
DPAD_DOWN = 0x5811,
DPAD_LEFT = 0x5812,
DPAD_RIGHT = 0x5813,
START = 0x5814,
BACK = 0x5815,
LTHUMB_PRESS = 0x5816,
RTHUMB_PRESS = 0x5817,
LTHUMB_UP = 0x5820,
LTHUMB_DOWN = 0x5821,
LTHUMB_RIGHT = 0x5822,
LTHUMB_LEFT = 0x5823,
LTHUMB_UPLEFT = 0x5824,
LTHUMB_UPRIGHT = 0x5825,
LTHUMB_DOWNRIGHT = 0x5826,
LTHUMB_DOWNLEFT = 0x5827,
RTHUMB_UP = 0x5830,
RTHUMB_DOWN = 0x5831,
RTHUMB_RIGHT = 0x5832,
RTHUMB_LEFT = 0x5833,
RTHUMB_UPLEFT = 0x5834,
RTHUMB_UPRIGHT = 0x5835,
RTHUMB_DOWNRIGHT = 0x5836,
RTHUMB_DOWNLEFT = 0x5837,
}
// Flags used in XINPUT_KEYSTROKE
// Correspond to log2(XINPUT_KEYSTROKE_...)
XINPUT_KEYSTROKE_BIT :: enum WORD {
KEYDOWN = 0,
KEYUP = 1,
REPEAT = 2,
}
XINPUT_KEYSTROKES :: distinct bit_set[XINPUT_KEYSTROKE_BIT;WORD]
// Structures used by XInput APIs
XINPUT_GAMEPAD :: struct {
wButtons: XINPUT_GAMEPAD_BUTTON,
bLeftTrigger: BYTE,
bRightTrigger: BYTE,
sThumbLX: SHORT,
sThumbLY: SHORT,
sThumbRX: SHORT,
sThumbRY: SHORT,
}
XINPUT_STATE :: struct {
dwPacketNumber: DWORD,
Gamepad: XINPUT_GAMEPAD,
}
XINPUT_VIBRATION :: struct {
wLeftMotorSpeed: WORD,
wRightMotorSpeed: WORD,
}
XINPUT_CAPABILITIES :: struct {
Type: XINPUT_DEVTYPE,
SubType: XINPUT_DEVSUBTYPE,
Flags: XINPUT_CAPS,
Gamepad: XINPUT_GAMEPAD,
Vibration: XINPUT_VIBRATION,
}
XINPUT_BATTERY_INFORMATION :: struct {
BatteryType: BATTERY_TYPE,
BatteryLevel: BATTERY_LEVEL,
}
XINPUT_KEYSTROKE :: struct {
VirtualKey: VK_PAD,
Unicode: WCHAR,
Flags: XINPUT_KEYSTROKES,
UserIndex: XUSER,
HidCode: BYTE,
}
// XInput APIs
@(default_calling_convention = "system")
foreign xinput {
XInputGetState :: proc(user: XUSER, pState: ^XINPUT_STATE) -> System_Error ---
XInputSetState :: proc(user: XUSER, pVibration: ^XINPUT_VIBRATION) -> System_Error ---
XInputGetCapabilities :: proc(user: XUSER, dwFlags: XINPUT_FLAG, pCapabilities: ^XINPUT_CAPABILITIES) -> System_Error ---
XInputEnable :: proc(enable: BOOL) ---
XInputGetAudioDeviceIds :: proc(user: XUSER, pRenderDeviceId: LPWSTR, pRenderCount: ^UINT, pCaptureDeviceId: LPWSTR, pCaptureCount: ^UINT) -> System_Error ---
XInputGetBatteryInformation :: proc(user: XUSER, devType: BATTERY_DEVTYPE, pBatteryInformation: ^XINPUT_BATTERY_INFORMATION) -> System_Error ---
XInputGetKeystroke :: proc(user: XUSER, dwReserved: DWORD, pKeystroke: ^XINPUT_KEYSTROKE) -> System_Error ---
XInputGetDSoundAudioDeviceGuids :: proc(user: XUSER, pDSoundRenderGuid: ^GUID, pDSoundCaptureGuid: ^GUID) -> System_Error ---
}
+1
View File
@@ -391,6 +391,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
fmt.assertf(alloc_error == nil, "Error allocating memory for task allocator #%i: %v", i, alloc_error)
when TRACKING_MEMORY {
mem.tracking_allocator_init(&task_memory_trackers[i], mem.rollback_stack_allocator(&task_allocators[i]))
task_memory_trackers[i].bad_free_callback = mem.tracking_allocator_bad_free_callback_add_to_array
}
}