aboutsummaryrefslogtreecommitdiffstats
path: root/code/lib/raddbg_markup.h
blob: 8459142c1b6c2100be8939363a0bd5d0f93c5ac8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)

#if !defined(RADDBG_MARKUP_H)
#define RADDBG_MARKUP_H

////////////////////////////////
//~ Implementation Overrides

#if !defined(RADDBG_MARKUP_VSNPRINTF)
# define RADDBG_MARKUP_DEFAULT_VSNPRINTF 1
# define RADDBG_MARKUP_VSNPRINTF vsnprintf
#endif

#if !defined(RADDBG_MARKUP_STL_TYPE_VIEWS)
# define RADDBG_MARKUP_STL_TYPE_VIEWS 1
#endif

////////////////////////////////
//~ Usage Macros

#if !defined(RADDBG_MARKUP_STUBS)
# define raddbg_is_attached(...)                      raddbg_is_attached__impl()
# define raddbg_thread_id(...)                        raddbg_thread_id__impl()
# define raddbg_thread_name(fmt, ...)                 raddbg_thread_name__impl(raddbg_thread_id(), (fmt), __VA_ARGS__)
# define raddbg_thread_id_name(id, fmt, ...)          raddbg_thread_name__impl((id), (fmt), __VA_ARGS__)
# define raddbg_thread_color_u32(u32)                 raddbg_thread_color__impl(raddbg_thread_id(), (u32))
# define raddbg_thread_color_rgba(r, g, b, a)         raddbg_thread_color__impl(raddbg_thread_id(), ((unsigned int)((r)*255) << 24) | ((unsigned int)((g)*255) << 16) | ((unsigned int)((b)*255) << 8) | ((unsigned int)(a)*255))
# define raddbg_thread_id_color_u32(id, u32)          raddbg_thread_color__impl((id), (u32))
# define raddbg_thread_id_color_rgba(id, r, g, b, a)  raddbg_thread_color__impl((id), ((unsigned int)((r)*255) << 24) | ((unsigned int)((g)*255) << 16) | ((unsigned int)((b)*255) << 8) | ((unsigned int)(a)*255))
# define raddbg_break(...)                            raddbg_break__impl()
# define raddbg_break_if(expr, ...)                   ((expr) ? raddbg_break__impl() : (void)0)
# define raddbg_watch(fmt, ...)                       raddbg_watch__impl((fmt), __VA_ARGS__)
# define raddbg_pin(expr, ...)                        /* NOTE(rjf): inspected by debugger ui - does not change program execution */
# define raddbg_log(fmt, ...)                         raddbg_log__impl((fmt), __VA_ARGS__)
# define raddbg_entry_point(...)                      raddbg_exe_data char raddbg_gen_data_id()[] = ("entry_point: \"" #__VA_ARGS__ "\"")
# define raddbg_type_view(type, ...)                  raddbg_exe_data char raddbg_gen_data_id()[] = ("type_view: {type: ```" #type "```, expr: ```" #__VA_ARGS__ "```}")
# define raddbg_add_breakpoint(ptr, size, r, w, x)    raddbg_add_or_remove_breakpoint__impl((ptr), (1), (size), (r), (w), (x))
# define raddbg_remove_breakpoint(ptr, size, r, w, x) raddbg_add_or_remove_breakpoint__impl((ptr), (0), (size), (r), (w), (x))
# define raddbg_annotate_vaddr_range(ptr, size, ...)  raddbg_annotate_vaddr_range__impl((ptr), (size), __VA_ARGS__)
#else
# define raddbg_is_attached(...)                      (0)
# define raddbg_thread_id(...)                        ((void)0)
# define raddbg_thread_name(fmt, ...)                 ((void)0)
# define raddbg_thread_id_name(id, fmt, ...)          ((void)0)
# define raddbg_thread_color_u32(u32)                 ((void)0)
# define raddbg_thread_color_rgba(r, g, b, a)         ((void)0)
# define raddbg_thread_id_color_u32(id, u32)          ((void)0)
# define raddbg_thread_id_color_rgba(id, r, g, b, a)  ((void)0)
# define raddbg_break(...)                            ((void)0)
# define raddbg_break_if(expr, ...)                   ((void)expr)
# define raddbg_watch(fmt, ...)                       ((void)0)
# define raddbg_pin(expr, ...)
# define raddbg_log(fmt, ...)                         ((void)0)
# define raddbg_entry_point(...)                      struct raddbg_gen_data_id(){int __unused__;}
# define raddbg_type_view(type, ...)                  struct raddbg_gen_data_id(){int __unused__;}
# define raddbg_add_breakpoint(ptr, size, r, w, x)    ((void)0)
# define raddbg_remove_breakpoint(ptr, size, r, w, x) ((void)0)
# define raddbg_annotate_vaddr_range(ptr, size, ...)  ((void)0)
#endif

////////////////////////////////
//~ Helpers

#define raddbg_glue_(a, b) a##b
#define raddbg_glue(a, b) raddbg_glue_(a, b)
#define raddbg_gen_data_id() raddbg_glue(raddbg_data__, __COUNTER__)

////////////////////////////////
//~ Global Symbols

#if !defined(RADDBG_MARKUP_STUBS)
extern unsigned char raddbg_is_attached_byte_marker[1];
int raddbg_is_attached__impl(void);
int raddbg_thread_id__impl(void);
void raddbg_thread_name__impl(int id, char *fmt, ...);
void raddbg_thread_color__impl(int id, unsigned int hexcode);
void raddbg_watch__impl(char *fmt, ...);
void raddbg_log__impl(char *fmt, ...);
void raddbg_add_or_remove_breakpoint__impl(void *ptr, int set, int size, int r, int w, int x);
void raddbg_annotate_vaddr_range__impl(void *ptr, unsigned __int64 size, char *fmt, ...);
#endif

////////////////////////////////
//~ Win32 Implementations

#if defined(_WIN32) && !defined(RADDBG_MARKUP_STUBS)

//- section allocating
#pragma section(".raddbg", read, write)
#define raddbg_exe_data __declspec(allocate(".raddbg"))

//- one-time implementations
#if defined(RADDBG_MARKUP_IMPLEMENTATION)

//- default includes
#if RADDBG_MARKUP_DEFAULT_VSNPRINTF
#include <stdio.h>
#endif

//- special section gets "is attached" byte
#pragma section(".rdbgia", read, write)
__declspec(allocate(".rdbgia")) unsigned char raddbg_is_attached_byte_marker[1] = {0};

//- types

typedef int BOOL;
typedef long LONG;
typedef unsigned long ULONG;
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
typedef unsigned long DWORD;
typedef wchar_t WCHAR;
typedef char const *LPCSTR;
typedef const WCHAR *LPCWSTR, *PCWSTR;
typedef LONG HRESULT;
typedef void *HANDLE;
struct HINSTANCE__;
typedef struct HINSTANCE__ *HMODULE;
typedef __int64 INT_PTR;
typedef INT_PTR (*FARPROC)();

//- prototypes

#include <stdarg.h>

#if defined(__cplusplus)
extern "C"
{
#endif
    __declspec(dllimport) HMODULE LoadLibraryA(LPCSTR name);
    __declspec(dllimport) FARPROC GetProcAddress(HMODULE module, LPCSTR name);
    __declspec(dllimport) BOOL FreeLibrary(HMODULE mod);
    __declspec(dllimport) HANDLE GetCurrentThread(void);
    __declspec(dllimport) DWORD GetCurrentThreadId(void);
    __declspec(dllimport) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR *lpArguments);
    __declspec(dllimport) void OutputDebugStringA(LPCSTR buffer);
    long long _InterlockedCompareExchange64(long long volatile*, long long, long long);
    long long _InterlockedExchangeAdd64(long long volatile*, long long);
#pragma intrinsic(_InterlockedCompareExchange64)
#pragma intrinsic(_InterlockedExchangeAdd64)
#if RADDBG_MARKUP_DEFAULT_VSNPRINTF
    int RADDBG_MARKUP_VSNPRINTF(char * const, unsigned long long const, const char * const, va_list);
#endif
#if defined(__cplusplus)
}
#endif

//- helpers

typedef struct RADDBG_MARKUP_UnicodeDecode RADDBG_MARKUP_UnicodeDecode;
struct RADDBG_MARKUP_UnicodeDecode
{
    unsigned __int32 inc;
    unsigned __int32 codepoint;
};
static __int8 raddbg_utf8_class[32] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2,3,3,4,5};

static RADDBG_MARKUP_UnicodeDecode
raddbg_decode_utf8(char *str, unsigned __int64 max)
{
    RADDBG_MARKUP_UnicodeDecode result = {1, 0xffffffff};
    unsigned __int8 byte = str[0];
    unsigned __int8 byte_class = raddbg_utf8_class[byte >> 3];
    switch(byte_class)
    {
        case 1:
        {
            result.codepoint = byte;
        }break;
        case 2:
        if(2 < max)
        {
            unsigned char cont_byte = str[1];
            if(raddbg_utf8_class[cont_byte >> 3] == 0)
            {
                result.codepoint = (byte & 0x0000001f) << 6;
                result.codepoint |= (cont_byte & 0x0000003f);
                result.inc = 2;
            }
        }break;
        case 3:
        if(2 < max)
        {
            unsigned char cont_byte[2] = {(unsigned char)str[1], (unsigned char)str[2]};
            if(raddbg_utf8_class[cont_byte[0] >> 3] == 0 &&
               raddbg_utf8_class[cont_byte[1] >> 3] == 0)
            {
                result.codepoint = (byte & 0x0000000f) << 12;
                result.codepoint |= ((cont_byte[0] & 0x0000003f) << 6);
                result.codepoint |=  (cont_byte[1] & 0x0000003f);
                result.inc = 3;
            }
        }break;
        case 4:
        if(3 < max)
        {
            unsigned char cont_byte[3] = {(unsigned char)str[1], (unsigned char)str[2], (unsigned char)str[3]};
            if(raddbg_utf8_class[cont_byte[0] >> 3] == 0 &&
               raddbg_utf8_class[cont_byte[1] >> 3] == 0 &&
               raddbg_utf8_class[cont_byte[2] >> 3] == 0)
            {
                result.codepoint = (byte & 0x00000007) << 18;
                result.codepoint |= ((cont_byte[0] & 0x0000003f) << 12);
                result.codepoint |= ((cont_byte[1] & 0x0000003f) <<  6);
                result.codepoint |=  (cont_byte[2] & 0x0000003f);
                result.inc = 4;
            }
        }
    }
    return result;
}

static unsigned __int32
raddbg_encode_utf16(wchar_t *str, unsigned __int32 codepoint)
{
    unsigned __int32 inc = 1;
    if(codepoint == 0xffffffff)
    {
        str[0] = (wchar_t)'?';
    }
    else if(codepoint < 0x10000)
    {
        str[0] = (wchar_t)codepoint;
    }
    else
    {
        unsigned __int32 v = codepoint - 0x10000;
        str[0] = (wchar_t)(0xD800 + (v >> 10));
        str[1] = (wchar_t)(0xDC00 + (v & 0x000003ff));
        inc = 2;
    }
    return inc;
}

//- implementations

int
raddbg_is_attached__impl(void)
{
    return !!raddbg_is_attached_byte_marker[0];
}

int
raddbg_thread_id__impl(void)
{
    DWORD result = GetCurrentThreadId();
    return result;
}

void
raddbg_thread_name__impl(int id, char *fmt, ...)
{
    // rjf: resolve variadic arguments
    char buffer[512] = {0};
    char *name = buffer;
    {
        va_list args;
        va_start(args, fmt);
        RADDBG_MARKUP_VSNPRINTF(buffer, sizeof(buffer), fmt, args);
        va_end(args);
    }
    
    // rjf: get windows 10 style procedure
    HRESULT (*SetThreadDescription_function)(HANDLE hThread, PCWSTR lpThreadDescription) = 0;
    {
        static HRESULT (*global_SetThreadDescription_function)(HANDLE hThread, PCWSTR lpThreadDescription);
        static volatile __int64 global_SetThreadDescription_init_started;
        static volatile __int64 global_SetThreadDescription_init_done;
        __int64 do_init = !_InterlockedCompareExchange64(&global_SetThreadDescription_init_started, 1, 0);
        if(do_init)
        {
            HMODULE module = LoadLibraryA("kernel32.dll");
            global_SetThreadDescription_function = (HRESULT (*)(HANDLE, PCWSTR))GetProcAddress(module, "SetThreadDescription");
            FreeLibrary(module);
            _InterlockedExchangeAdd64(&global_SetThreadDescription_init_done, 1);
        }
        for(;_InterlockedExchangeAdd64(&global_SetThreadDescription_init_done, 0) == 0;)
        {
            // NOTE(rjf): busy-loop, until init is done
        }
        SetThreadDescription_function = global_SetThreadDescription_function;
    }
    
    // rjf: set thread name, windows 10 style
    if(SetThreadDescription_function && id == GetCurrentThreadId())
    {
        WCHAR buffer16[1024] = {0};
        int name_length = 0;
        for(;name[name_length]; name_length += 1);
        int write_offset = 0;
        for(int idx = 0; idx < name_length;)
        {
            RADDBG_MARKUP_UnicodeDecode decode = raddbg_decode_utf8(name+idx, name_length-idx);
            write_offset += raddbg_encode_utf16(buffer16 + write_offset, decode.codepoint);
            idx += decode.inc;
        }
        SetThreadDescription_function(GetCurrentThread(), buffer16);
    }
    
    // rjf: set thread name, raise-exception style
    {
#pragma pack(push, 8)
        typedef struct THREADNAME_INFO THREADNAME_INFO;
        struct THREADNAME_INFO
        {
            DWORD dwType;
            LPCSTR szName;
            DWORD dwThreadID;
            DWORD dwFlags;
        };
#pragma pack(pop)
        THREADNAME_INFO info;
        info.dwType = 0x1000;
        info.szName = name;
        info.dwThreadID = id;
        info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
        __try
        {
            RaiseException(0x406D1388u, 0, sizeof(info) / sizeof(void *), (const ULONG_PTR *)&info);
        }
        __except(1)
        {
        }
#pragma warning(pop)
    }
}

void
raddbg_thread_color__impl(int id, unsigned int hexcode)
{
    if(raddbg_is_attached())
    {
#pragma pack(push, 8)
        typedef struct RADDBG_ThreadColorInfo RADDBG_ThreadColorInfo;
        struct RADDBG_ThreadColorInfo
        {
            DWORD dwThreadID;
            DWORD _pad_0;
            DWORD rgba;
            DWORD _pad_1;
        };
#pragma pack(pop)
        RADDBG_ThreadColorInfo info;
        info.dwThreadID = id;
        info.rgba = hexcode;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
        __try
        {
            RaiseException(0x00524144u, 0, sizeof(info) / sizeof(void *), (const ULONG_PTR *)&info);
        }
        __except(1)
        {
        }
#pragma warning(pop)
    }
}

#define raddbg_break__impl() (__debugbreak())

void
raddbg_watch__impl(char *fmt, ...)
{
    // TODO(rjf)
}

void
raddbg_log__impl(char *fmt, ...)
{
    // rjf: resolve variadic arguments
    char buffer[4096];
    {
        va_list args;
        va_start(args, fmt);
        RADDBG_MARKUP_VSNPRINTF(buffer, sizeof(buffer), fmt, args);
        va_end(args);
    }
    
    // rjf: output debug string
    OutputDebugStringA(buffer);
}

void
raddbg_add_or_remove_breakpoint__impl(void *ptr, int set, int size, int r, int w, int x)
{
    if(raddbg_is_attached())
    {
#pragma pack(push, 8)
        typedef struct RADDBG_AddBreakpointInfo RADDBG_AddBreakpointInfo;
        struct RADDBG_AddBreakpointInfo
        {
            unsigned __int64 vaddr;
            unsigned __int64 size;
            unsigned __int64 r;
            unsigned __int64 w;
            unsigned __int64 x;
            unsigned __int64 add;
        };
#pragma pack(pop)
        RADDBG_AddBreakpointInfo info;
        info.vaddr = (unsigned __int64)ptr;
        info.size  = size;
        info.r     = r;
        info.w     = w;
        info.x     = x;
        info.add   = set;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
        __try
        {
            RaiseException(0x00524145u, 0, sizeof(info) / sizeof(void *), (const ULONG_PTR *)&info);
        }
        __except(1)
        {
        }
#pragma warning(pop)
    }
}

void
raddbg_annotate_vaddr_range__impl(void *ptr, unsigned __int64 size, char *fmt, ...)
{
    if(raddbg_is_attached())
    {
        // rjf: resolve variadic arguments
        char buffer[4096];
        int buffer_size = 0;
        {
            va_list args;
            va_start(args, fmt);
            buffer_size = RADDBG_MARKUP_VSNPRINTF(buffer, sizeof(buffer), fmt, args);
            buffer_size = ((buffer_size < 0) ? 0 :
                           (buffer_size > sizeof(buffer)) ? sizeof(buffer) :
                           buffer_size);
            va_end(args);
        }
        
        // rjf: send annotation info via exception
#pragma pack(push, 8)
        typedef struct RADDBG_VaddrRangeAnnotationInfo RADDBG_VaddrRangeAnnotationInfo;
        struct RADDBG_VaddrRangeAnnotationInfo
        {
            unsigned __int64 vaddr;
            unsigned __int64 size;
            void *name;
            unsigned __int64 name_size;
        };
#pragma pack(pop)
        RADDBG_VaddrRangeAnnotationInfo info;
        info.vaddr     = (unsigned __int64)ptr;
        info.size      = size;
        info.name      = buffer;
        info.name_size = buffer_size;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
        __try
        {
            RaiseException(0x00524156u, 0, sizeof(info) / sizeof(void *), (const ULONG_PTR *)&info);
        }
        __except(1)
        {
        }
#pragma warning(pop)
    }
}

#endif // defined(RADDBG_MARKUP_IMPLEMENTATION)
#endif // defined(_WIN32) && !defined(RADDBG_MARKUP_STUBS)

////////////////////////////////
//~ Win32 STL Type Views

#if defined(_WIN32) && defined(RADDBG_MARKUP_IMPLEMENTATION) && RADDBG_MARKUP_STL_TYPE_VIEWS
# if defined(_VECTOR_)
raddbg_type_view(std::vector<?>, slice(_Mypair._Myval2));
# endif
# if defined(_MEMORY_)
raddbg_type_view(std::unique_ptr<?>, _Mypair._Myval2);
# endif
# if defined(_STRING_)
raddbg_type_view(std::basic_string<?>, _Mypair._Myval2._Myres <= 15 ? _Mypair._Myval2._Bx._Buf : array(_Mypair._Myval2._Bx._Ptr, _Mypair._Myval2._Mysize));
# endif
# if defined(_STRING_VIEW_)
raddbg_type_view(std::basic_string_view<?>, array(_Mydata, _Mysize));
# endif
#endif // defined(_WIN32) && defined(RADDBG_MARKUP_IMPLEMENTATION) && RADDBG_MARKUP_STL_TYPE_VIEWS

#endif // RADDBG_MARKUP_H