2021-11-03 18:15:46 -04:00
# include "util.js.h"
2024-01-03 12:25:34 -05:00
# include "bip39.h"
2023-03-07 12:50:17 -05:00
# include "log.h"
2022-06-04 13:04:51 -04:00
# include "mem.h"
2021-11-03 18:15:46 -04:00
# include "task.h"
2021-12-27 15:49:07 -05:00
# include "trace.h"
2021-11-03 18:15:46 -04:00
2023-05-21 17:36:51 -04:00
# include "backtrace.h"
# include "openssl/sha.h"
# include "picohttpparser.h"
# include "sodium/utils.h"
# include "uv.h"
2021-12-27 15:49:07 -05:00
# include <string.h>
2023-03-29 18:25:17 -04:00
# if defined(__ANDROID__)
# include <unwind.h>
2023-10-25 22:56:33 -04:00
# elif !defined(_WIN32) && !defined(__HAIKU__)
2023-02-18 16:00:39 -05:00
# include <execinfo.h>
# endif
2024-11-06 20:29:48 -05:00
# define tf_countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
2022-01-26 20:15:54 -05:00
static JSValue _util_utf8_encode ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
size_t length = 0 ;
const char * value = JS_ToCStringLen ( context , & length , argv [ 0 ] ) ;
2023-01-28 16:59:36 -05:00
JSValue typed_array = tf_util_new_uint8_array ( context , ( const uint8_t * ) value , length ) ;
2022-01-26 20:15:54 -05:00
JS_FreeCString ( context , value ) ;
2023-01-28 16:59:36 -05:00
return typed_array ;
2022-01-26 20:15:54 -05:00
}
2021-11-03 18:15:46 -04:00
static JSValue _util_utf8_decode ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_NULL ;
size_t length ;
if ( JS_IsString ( argv [ 0 ] ) )
{
result = JS_DupValue ( context , argv [ 0 ] ) ;
}
else
{
uint8_t * array = tf_util_try_get_array_buffer ( context , & length , argv [ 0 ] ) ;
if ( array )
{
result = JS_NewStringLen ( context , ( const char * ) array , length ) ;
}
else
{
2024-03-12 21:44:20 -04:00
size_t offset = 0 ;
size_t element_size = 0 ;
2021-11-03 18:15:46 -04:00
JSValue buffer = tf_util_try_get_typed_array_buffer ( context , argv [ 0 ] , & offset , & length , & element_size ) ;
2024-03-12 21:44:20 -04:00
size_t size = 0 ;
2021-11-03 18:15:46 -04:00
if ( ! JS_IsException ( buffer ) )
{
array = tf_util_try_get_array_buffer ( context , & size , buffer ) ;
if ( array )
{
result = JS_NewStringLen ( context , ( const char * ) array , size ) ;
}
}
JS_FreeValue ( context , buffer ) ;
}
}
return result ;
}
JSValue tf_util_utf8_decode ( JSContext * context , JSValue value )
{
return _util_utf8_decode ( context , JS_NULL , 1 , & value ) ;
}
2022-09-28 19:52:44 -04:00
static JSValue _util_base64_encode ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_UNDEFINED ;
2024-01-03 12:25:34 -05:00
JSValue buffer = JS_UNDEFINED ;
2022-09-28 19:52:44 -04:00
size_t length = 0 ;
2023-01-28 16:59:36 -05:00
uint8_t * array = tf_util_try_get_array_buffer ( context , & length , argv [ 0 ] ) ;
2024-01-03 12:25:34 -05:00
if ( ! array )
{
size_t offset ;
size_t element_size ;
buffer = tf_util_try_get_typed_array_buffer ( context , argv [ 0 ] , & offset , & length , & element_size ) ;
if ( ! JS_IsException ( buffer ) )
{
array = tf_util_try_get_array_buffer ( context , & length , buffer ) ;
}
}
2023-01-28 16:59:36 -05:00
if ( array )
2022-09-28 19:52:44 -04:00
{
2023-01-28 16:59:36 -05:00
char * encoded = tf_malloc ( length * 4 ) ;
2023-02-13 22:15:24 -05:00
int r = tf_base64_encode ( array , length , encoded , length * 4 ) ;
2023-01-28 16:59:36 -05:00
if ( r > = 0 )
{
result = JS_NewStringLen ( context , encoded , r ) ;
}
tf_free ( encoded ) ;
}
else
{
const char * value = JS_ToCStringLen ( context , & length , argv [ 0 ] ) ;
char * encoded = tf_malloc ( length * 4 ) ;
2023-02-13 22:15:24 -05:00
int r = tf_base64_encode ( ( const uint8_t * ) value , length , encoded , length * 4 ) ;
2023-01-28 16:59:36 -05:00
if ( r > = 0 )
{
result = JS_NewStringLen ( context , encoded , r ) ;
}
tf_free ( encoded ) ;
JS_FreeCString ( context , value ) ;
2022-09-28 19:52:44 -04:00
}
2024-01-03 12:25:34 -05:00
JS_FreeValue ( context , buffer ) ;
2022-09-28 19:52:44 -04:00
return result ;
}
static JSValue _util_base64_decode ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_UNDEFINED ;
size_t length = 0 ;
const char * value = JS_ToCStringLen ( context , & length , argv [ 0 ] ) ;
2024-01-03 12:25:34 -05:00
uint8_t * decoded = tf_malloc ( length ) ;
2022-09-28 19:52:44 -04:00
2024-01-03 12:25:34 -05:00
int r = tf_base64_decode ( value , length , decoded , length ) ;
2022-09-28 19:52:44 -04:00
if ( r > = 0 )
{
2024-01-03 12:25:34 -05:00
result = tf_util_new_uint8_array ( context , decoded , r ) ;
2022-09-28 19:52:44 -04:00
}
2024-01-03 12:25:34 -05:00
tf_free ( decoded ) ;
2022-09-28 19:52:44 -04:00
JS_FreeCString ( context , value ) ;
return result ;
}
2024-01-03 12:25:34 -05:00
static JSValue _util_bip39_words ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_UNDEFINED ;
JSValue buffer = JS_UNDEFINED ;
size_t length ;
uint8_t * array = tf_util_try_get_array_buffer ( context , & length , argv [ 0 ] ) ;
if ( ! array )
{
size_t offset ;
size_t element_size ;
buffer = tf_util_try_get_typed_array_buffer ( context , argv [ 0 ] , & offset , & length , & element_size ) ;
if ( ! JS_IsException ( buffer ) )
{
array = tf_util_try_get_array_buffer ( context , & length , buffer ) ;
}
}
char words [ 2048 ] = " " ;
if ( array & & tf_bip39_bytes_to_words ( array , length , words , sizeof ( words ) ) )
{
result = JS_NewString ( context , words ) ;
}
JS_FreeValue ( context , buffer ) ;
return result ;
}
static JSValue _util_bip39_bytes ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
const char * words = JS_ToCString ( context , argv [ 0 ] ) ;
2024-01-03 20:17:30 -05:00
uint8_t bytes [ 32 ] = { 0 } ;
2024-01-03 12:25:34 -05:00
bool success = tf_bip39_words_to_bytes ( words , bytes , sizeof ( bytes ) ) ;
JS_FreeCString ( context , words ) ;
if ( success )
{
return tf_util_new_uint8_array ( context , bytes , sizeof ( bytes ) ) ;
}
return JS_UNDEFINED ;
}
2021-11-03 18:28:25 -04:00
uint8_t * tf_util_try_get_array_buffer ( JSContext * context , size_t * psize , JSValueConst obj )
2021-11-03 18:15:46 -04:00
{
2021-11-03 18:28:25 -04:00
uint8_t * result = JS_GetArrayBuffer ( context , psize , obj ) ;
JS_FreeValue ( context , JS_GetException ( context ) ) ;
2021-11-03 18:15:46 -04:00
return result ;
}
2021-11-03 18:28:25 -04:00
JSValue tf_util_try_get_typed_array_buffer ( JSContext * context , JSValueConst obj , size_t * pbyte_offset , size_t * pbyte_length , size_t * pbytes_per_element )
2021-11-03 18:15:46 -04:00
{
2021-11-03 18:28:25 -04:00
JSValue result = JS_GetTypedArrayBuffer ( context , obj , pbyte_offset , pbyte_length , pbytes_per_element ) ;
JS_FreeValue ( context , JS_GetException ( context ) ) ;
2021-11-03 18:15:46 -04:00
return result ;
}
2024-02-10 11:50:00 -05:00
static JSValue _util_print ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
2021-11-03 18:15:46 -04:00
{
tf_task_t * task = JS_GetContextOpaque ( context ) ;
if ( task )
{
2023-03-07 12:50:17 -05:00
tf_printf ( " Task[%p:%s]> " , task , tf_task_get_name ( task ) ) ;
2022-02-13 17:39:22 -05:00
tf_task_print ( task , argc , argv ) ;
2021-11-03 18:15:46 -04:00
}
for ( int i = 0 ; i < argc ; + + i )
{
if ( JS_IsNull ( argv [ i ] ) )
{
2023-03-07 12:50:17 -05:00
tf_printf ( " null " ) ;
2021-11-03 18:15:46 -04:00
}
else
{
const char * value = JS_ToCString ( context , argv [ i ] ) ;
2023-03-07 12:50:17 -05:00
tf_printf ( " %s " , value ) ;
2021-11-03 18:15:46 -04:00
JS_FreeCString ( context , value ) ;
}
}
2023-03-07 12:50:17 -05:00
tf_printf ( " \n " ) ;
2021-11-03 18:15:46 -04:00
return JS_NULL ;
}
2021-12-27 16:48:16 -05:00
bool tf_util_report_error ( JSContext * context , JSValue value )
2021-11-03 18:28:25 -04:00
{
2021-12-27 16:48:16 -05:00
bool is_error = false ;
2024-08-21 12:53:38 -04:00
tf_task_t * task = tf_task_get ( context ) ;
2021-11-03 18:28:25 -04:00
if ( JS_IsError ( context , value ) )
{
const char * string = JS_ToCString ( context , value ) ;
2023-03-07 12:50:17 -05:00
tf_printf ( " ERROR: %s \n " , string ) ;
2021-11-03 18:28:25 -04:00
JS_FreeCString ( context , string ) ;
JSValue stack = JS_GetPropertyStr ( context , value , " stack " ) ;
if ( ! JS_IsUndefined ( stack ) )
{
const char * stack_str = JS_ToCString ( context , stack ) ;
2023-03-07 12:50:17 -05:00
tf_printf ( " %s \n " , stack_str ) ;
2021-11-03 18:28:25 -04:00
JS_FreeCString ( context , stack_str ) ;
}
JS_FreeValue ( context , stack ) ;
2023-03-19 20:29:46 -04:00
tf_task_send_error_to_parent ( task , value ) ;
2021-12-27 16:48:16 -05:00
is_error = true ;
2021-11-03 18:28:25 -04:00
}
else if ( JS_IsException ( value ) )
{
2023-12-20 20:27:57 -05:00
if ( ! tf_task_send_error_to_parent ( task , value ) )
{
JSValue exception = JS_GetException ( context ) ;
tf_util_report_error ( context , exception ) ;
JS_FreeValue ( context , exception ) ;
}
2021-12-27 16:48:16 -05:00
is_error = true ;
2021-11-03 18:28:25 -04:00
}
2024-08-21 12:53:38 -04:00
tf_task_check_jobs ( task ) ;
2021-12-27 16:48:16 -05:00
return is_error ;
2021-11-03 18:28:25 -04:00
}
2023-07-22 21:12:11 -04:00
static JSValue _util_parseHttpResponse ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_UNDEFINED ;
int status = 0 ;
int minor_version = 0 ;
const char * message = NULL ;
size_t message_length = 0 ;
struct phr_header headers [ 100 ] ;
size_t header_count = sizeof ( headers ) / sizeof ( * headers ) ;
int previous_length = 0 ;
JS_ToInt32 ( context , & previous_length , argv [ 1 ] ) ;
JSValue buffer = JS_UNDEFINED ;
size_t length ;
uint8_t * array = tf_util_try_get_array_buffer ( context , & length , argv [ 0 ] ) ;
if ( ! array )
{
size_t offset ;
size_t element_size ;
buffer = tf_util_try_get_typed_array_buffer ( context , argv [ 0 ] , & offset , & length , & element_size ) ;
if ( ! JS_IsException ( buffer ) )
{
array = tf_util_try_get_array_buffer ( context , & length , buffer ) ;
}
}
if ( array )
{
int parse_result = phr_parse_response ( ( const char * ) array , length , & minor_version , & status , & message , & message_length , headers , & header_count , previous_length ) ;
if ( parse_result > 0 )
{
result = JS_NewObject ( context ) ;
JS_SetPropertyStr ( context , result , " bytes_parsed " , JS_NewInt32 ( context , parse_result ) ) ;
JS_SetPropertyStr ( context , result , " minor_version " , JS_NewInt32 ( context , minor_version ) ) ;
JS_SetPropertyStr ( context , result , " status " , JS_NewInt32 ( context , status ) ) ;
JS_SetPropertyStr ( context , result , " message " , JS_NewStringLen ( context , message , message_length ) ) ;
JSValue header_object = JS_NewObject ( context ) ;
for ( int i = 0 ; i < ( int ) header_count ; i + + )
{
char name [ 256 ] ;
snprintf ( name , sizeof ( name ) , " %.*s " , ( int ) headers [ i ] . name_len , headers [ i ] . name ) ;
JS_SetPropertyStr ( context , header_object , name , JS_NewStringLen ( context , headers [ i ] . value , headers [ i ] . value_len ) ) ;
}
JS_SetPropertyStr ( context , result , " headers " , header_object ) ;
}
else
{
result = JS_NewInt32 ( context , parse_result ) ;
}
}
else
{
result = JS_ThrowTypeError ( context , " Could not convert argument to array. " ) ;
}
JS_FreeValue ( context , buffer ) ;
2022-12-31 11:47:10 -05:00
return result ;
}
2024-11-06 20:29:48 -05:00
# if defined(__APPLE__)
# include <TargetConditionals.h>
# endif
static bool _is_mobile ( )
{
# if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IPHONE)
return true ;
# else
return false ;
# endif
}
static JSValue _util_defaultGlobalSettings ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
typedef struct _setting_t
{
const char * name ;
const char * type ;
const char * description ;
JSValue default_value ;
} setting_t ;
const setting_t k_settings [ ] = {
{ . name = " code_of_conduct " , . type = " textarea " , . description = " Code of conduct presented at sign-in. " } ,
{ . name = " blob_fetch_age_seconds " ,
. type = " integer " ,
. description = " Only blobs mentioned more recently than this age will be automatically fetched. " ,
. default_value = _is_mobile ( ) ? JS_NewInt32 ( context , ( int ) ( 0.5f * 365 * 24 * 60 * 60 ) ) : JS_UNDEFINED } ,
{ . name = " blob_expire_age_seconds " ,
. type = " integer " ,
. description = " Blobs older than this will be automatically deleted. " ,
. default_value = _is_mobile ( ) ? JS_NewInt32 ( context , ( int ) ( 1.0f * 365 * 24 * 60 * 60 ) ) : JS_UNDEFINED } ,
{ . name = " fetch_hosts " , . type = " string " , . description = " Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty. " } ,
{ . name = " http_redirect " , . type = " string " , . description = " If connecting by HTTP and HTTPS is configured, Location header prefix (ie, \" http://example.com \" ) " } ,
{ . name = " index " , . type = " string " , . description = " Default path. " , . default_value = JS_NewString ( context , " /~core/apps " ) } ,
{ . name = " index_map " , . type = " textarea " , . description = " Mappings from hostname to redirect path, one per line, as in: \" www.tildefriends.net=/~core/index/ \" " } ,
{ . name = " peer_exchange " ,
. type = " boolean " ,
. description = " Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance. " ,
. default_value = JS_FALSE } ,
{ . name = " replicator " , . type = " boolean " , . description = " Enable message and blob replication. " , . default_value = JS_TRUE } ,
{ . name = " room " , . type = " boolean " , . description = " Enable peers to tunnel through this instance as a room. " , . default_value = JS_TRUE } ,
{ . name = " room_name " , . type = " string " , . description = " Name of the room. " , . default_value = JS_NewString ( context , " tilde friends tunnel " ) } ,
{ . name = " seeds_host " , . type = " string " , . description = " Hostname for seed connections. " , . default_value = JS_NewString ( context , " seeds.tildefriends.net " ) } ,
{ . name = " account_registration " , . type = " boolean " , . description = " Allow registration of new accounts. " , . default_value = JS_TRUE } ,
} ;
JSValue settings = JS_NewObject ( context ) ;
for ( int i = 0 ; i < tf_countof ( k_settings ) ; i + + )
{
JSValue entry = JS_NewObject ( context ) ;
JS_SetPropertyStr ( context , entry , " type " , JS_NewString ( context , k_settings [ i ] . type ) ) ;
JS_SetPropertyStr ( context , entry , " description " , JS_NewString ( context , k_settings [ i ] . description ) ) ;
JS_SetPropertyStr ( context , entry , " default_value " , k_settings [ i ] . default_value ) ;
JS_SetPropertyStr ( context , settings , k_settings [ i ] . name , entry ) ;
}
return settings ;
}
2023-01-28 16:59:36 -05:00
JSValue tf_util_new_uint8_array ( JSContext * context , const uint8_t * data , size_t size )
{
JSValue array_buffer = JS_NewArrayBufferCopy ( context , data , size ) ;
JSValue global = JS_GetGlobalObject ( context ) ;
JSValue constructor = JS_GetPropertyStr ( context , global , " Uint8Array " ) ;
JSValue result = JS_CallConstructor ( context , constructor , 1 , & array_buffer ) ;
JS_FreeValue ( context , constructor ) ;
JS_FreeValue ( context , global ) ;
JS_FreeValue ( context , array_buffer ) ;
return result ;
}
2021-11-03 18:15:46 -04:00
void tf_util_register ( JSContext * context )
{
JSValue global = JS_GetGlobalObject ( context ) ;
JS_SetPropertyStr ( context , global , " utf8Decode " , JS_NewCFunction ( context , _util_utf8_decode , " utf8Decode " , 1 ) ) ;
2022-01-26 20:15:54 -05:00
JS_SetPropertyStr ( context , global , " utf8Encode " , JS_NewCFunction ( context , _util_utf8_encode , " utf8Encode " , 1 ) ) ;
2022-09-28 19:52:44 -04:00
JS_SetPropertyStr ( context , global , " base64Decode " , JS_NewCFunction ( context , _util_base64_decode , " base64Decode " , 1 ) ) ;
JS_SetPropertyStr ( context , global , " base64Encode " , JS_NewCFunction ( context , _util_base64_encode , " base64Encode " , 1 ) ) ;
2024-01-03 12:25:34 -05:00
JS_SetPropertyStr ( context , global , " bip39Words " , JS_NewCFunction ( context , _util_bip39_words , " bip39Words " , 1 ) ) ;
JS_SetPropertyStr ( context , global , " bip39Bytes " , JS_NewCFunction ( context , _util_bip39_bytes , " bip39Bytes " , 1 ) ) ;
2021-11-03 18:15:46 -04:00
JS_SetPropertyStr ( context , global , " print " , JS_NewCFunction ( context , _util_print , " print " , 1 ) ) ;
2023-07-22 21:12:11 -04:00
JS_SetPropertyStr ( context , global , " parseHttpResponse " , JS_NewCFunction ( context , _util_parseHttpResponse , " parseHttpResponse " , 2 ) ) ;
2024-11-06 20:29:48 -05:00
JS_SetPropertyStr ( context , global , " defaultGlobalSettings " , JS_NewCFunction ( context , _util_defaultGlobalSettings , " defaultGlobalSettings " , 2 ) ) ;
2021-11-03 18:15:46 -04:00
JS_FreeValue ( context , global ) ;
}
2022-07-09 11:13:35 -04:00
int tf_util_get_length ( JSContext * context , JSValue value )
{
JSValue length = JS_GetPropertyStr ( context , value , " length " ) ;
int result = 0 ;
JS_ToInt32 ( context , & result , length ) ;
JS_FreeValue ( context , length ) ;
return result ;
}
2022-07-11 21:51:15 -04:00
int tf_util_insert_index ( const void * key , const void * base , size_t count , size_t size , int ( * compare ) ( const void * , const void * ) )
{
int lower = 0 ;
int upper = count ;
while ( lower < upper & & lower < ( int ) count )
{
int guess = ( lower + upper ) / 2 ;
int result = compare ( key , ( ( char * ) base ) + size * guess ) ;
if ( result < 0 )
{
upper = guess ;
}
else if ( result > 0 )
{
lower = guess + 1 ;
}
else
{
return guess ;
}
} ;
return lower ;
}
2023-02-13 22:15:24 -05:00
size_t tf_base64_encode ( const uint8_t * source , size_t source_length , char * out , size_t out_length )
{
sodium_bin2base64 ( out , out_length , source , source_length , sodium_base64_VARIANT_ORIGINAL ) ;
return sodium_base64_ENCODED_LEN ( source_length , sodium_base64_VARIANT_ORIGINAL ) - 1 ;
}
size_t tf_base64_decode ( const char * source , size_t source_length , uint8_t * out , size_t out_length )
{
size_t actual_length = 0 ;
return sodium_base642bin ( out , out_length , source , source_length , NULL , & actual_length , NULL , sodium_base64_VARIANT_ORIGINAL ) = = 0 ? actual_length : 0 ;
}
2023-02-18 16:00:39 -05:00
static int _tf_util_backtrace_callback ( void * data , uintptr_t pc , const char * filename , int line_number , const char * function )
{
char * * stack = data ;
char line [ 256 ] ;
int length = snprintf ( line , sizeof ( line ) , " %p %s:%d %s \n " , ( void * ) pc , filename , line_number , function ) ;
int current = * stack ? strlen ( * stack ) : 0 ;
* stack = tf_resize_vec ( * stack , current + length + 1 ) ;
memcpy ( * stack + current , line , length + 1 ) ;
return 0 ;
}
static void _tf_util_backtrace_error ( void * data , const char * message , int error )
{
char * * stack = data ;
if ( message )
{
2023-10-25 22:56:33 -04:00
int length = strlen ( message ) ;
2023-02-18 16:00:39 -05:00
int current = * stack ? strlen ( * stack ) : 0 ;
* stack = tf_resize_vec ( * stack , current + length + 1 ) ;
memcpy ( * stack + current , message , length + 1 ) ;
}
}
const char * tf_util_backtrace_to_string ( void * const * buffer , int count )
{
extern struct backtrace_state * g_backtrace_state ;
char * string = NULL ;
for ( int i = 0 ; i < count ; i + + )
{
2024-02-15 18:35:01 -05:00
backtrace_pcinfo ( g_backtrace_state , ( uintptr_t ) buffer [ i ] , _tf_util_backtrace_callback , _tf_util_backtrace_error , & string ) ;
2023-02-18 16:00:39 -05:00
}
return string ;
2023-04-19 19:05:59 -04:00
}
static int _tf_util_backtrace_single_callback ( void * data , uintptr_t pc , const char * filename , int line_number , const char * function )
{
char * * stack = data ;
char line [ 256 ] ;
int length = snprintf ( line , sizeof ( line ) , " %s " , function ) ;
int current = * stack ? strlen ( * stack ) : 0 ;
* stack = tf_resize_vec ( * stack , current + length + 1 ) ;
memcpy ( * stack + current , line , length + 1 ) ;
return 0 ;
}
const char * tf_util_function_to_string ( void * function )
{
extern struct backtrace_state * g_backtrace_state ;
char * string = NULL ;
2024-02-15 18:35:01 -05:00
backtrace_pcinfo ( g_backtrace_state , ( uintptr_t ) function , _tf_util_backtrace_single_callback , _tf_util_backtrace_error , & string ) ;
2023-04-19 19:05:59 -04:00
return string ;
2023-02-18 16:00:39 -05:00
}
const char * tf_util_backtrace_string ( )
{
void * buffer [ 32 ] ;
int count = tf_util_backtrace ( buffer , sizeof ( buffer ) / sizeof ( * buffer ) ) ;
return tf_util_backtrace_to_string ( buffer , count ) ;
}
2024-06-20 19:49:21 -04:00
void tf_util_print_backtrace ( )
{
const char * bt = tf_util_backtrace_string ( ) ;
2024-06-26 20:20:34 -04:00
if ( bt )
{
tf_printf ( " %s \n " , bt ) ;
}
2024-06-20 19:49:21 -04:00
tf_free ( ( void * ) bt ) ;
}
2023-03-29 18:25:17 -04:00
# if defined(__ANDROID__)
typedef struct _android_backtrace_t
{
void * * current ;
void * * end ;
} android_backtrace_t ;
static _Unwind_Reason_Code _android_unwind_callback ( struct _Unwind_Context * context , void * arg )
{
android_backtrace_t * state = arg ;
uintptr_t pc = _Unwind_GetIP ( context ) ;
if ( pc )
{
if ( state - > current = = state - > end )
{
return _URC_END_OF_STACK ;
}
else
{
* state - > current + + = ( void * ) pc ;
}
}
return _URC_NO_REASON ;
}
# endif
2023-02-18 16:00:39 -05:00
int tf_util_backtrace ( void * * buffer , int count )
{
# ifdef _WIN32
return CaptureStackBackTrace ( 0 , count , buffer , NULL ) ;
2023-03-29 18:25:17 -04:00
# elif defined(__ANDROID__)
android_backtrace_t state = { . current = buffer , . end = buffer + count } ;
_Unwind_Backtrace ( _android_unwind_callback , & state ) ;
return state . current - buffer ;
2023-10-25 22:56:33 -04:00
# elif defined(__HAIKU__)
return 0 ;
2023-02-18 16:00:39 -05:00
# else
2023-03-29 18:25:17 -04:00
return backtrace ( buffer , count ) ;
2023-02-18 16:00:39 -05:00
# endif
}