From dff2733c178ff7939bebfe3d08c241fe63c56c1c Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 11 Dec 2024 16:23:19 +0100 Subject: [PATCH] * NSInvocation does not raise anymore during -init as noone should raise during init * NSAutoreleasePool name can now be 48-1 bytes long added animation.html generator for debugging some tests ("hidden" in test/TAO/forward) --- .mulle/share/env/environment-plugin.sh | 2 +- .mulle/share/env/version | 2 +- .mulle/share/sde/version/mulle-sde/cmake | 2 +- README.md | 2 +- RELEASENOTES.md | 3 + cmake/share/InstallRpath.cmake | 13 ++- src/MulleObjCIntegralType.h | 1 + src/class/NSAutoreleasePool.h | 8 +- src/class/NSAutoreleasePool.m | 27 ++++-- src/class/NSInvocation.h | 2 +- src/class/NSInvocation.m | 24 ++---- src/class/NSObject.m | 5 ++ src/class/NSThread.m | 6 +- src/reflect/_MulleObjC-versioncheck.h | 6 +- test/.mulle/share/env/environment-plugin.sh | 2 +- test/.mulle/share/env/version | 2 +- test/NSAutoreleasePool/pooldump.m | 2 +- test/TAO/forward/TAOStrategyTest.inc | 21 ++++- test/TAO/forward/generate-animation.sh | 96 ++++++++++++++++++--- test/TAO/forward/pooldump-dot.awk | 18 +++- 20 files changed, 190 insertions(+), 54 deletions(-) diff --git a/.mulle/share/env/environment-plugin.sh b/.mulle/share/env/environment-plugin.sh index e35d3a2f..33b0fbb6 100644 --- a/.mulle/share/env/environment-plugin.sh +++ b/.mulle/share/env/environment-plugin.sh @@ -22,6 +22,6 @@ export MULLE_SOURCETREE_SYMLINK='YES' # # # -export MULLE_SDE_INSTALLED_VERSION="3.2.0" +export MULLE_SDE_INSTALLED_VERSION="3.2.2" diff --git a/.mulle/share/env/version b/.mulle/share/env/version index 03f488b0..c7cb1311 100644 --- a/.mulle/share/env/version +++ b/.mulle/share/env/version @@ -1 +1 @@ -5.3.0 +5.3.1 diff --git a/.mulle/share/sde/version/mulle-sde/cmake b/.mulle/share/sde/version/mulle-sde/cmake index 697f087f..48f7a71d 100644 --- a/.mulle/share/sde/version/mulle-sde/cmake +++ b/.mulle/share/sde/version/mulle-sde/cmake @@ -1 +1 @@ -0.28.0 +0.28.1 diff --git a/README.md b/README.md index cf008d92..67b074d9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ only and for instance not on ``. | Release Version | Release Notes |-------------------------------------------------------|-------------- -| ![Mulle kybernetiK tag](https://img.shields.io/github/tag/mulle-objc/MulleObjC.svg?branch=release) [![Build Status](https://github.com/mulle-objc/MulleObjC/workflows/CI/badge.svg?branch=release)](//github.com/mulle-objc/MulleObjC/actions) | [RELEASENOTES](RELEASENOTES.md) | +| ![Mulle kybernetiK tag](https://img.shields.io/github/tag/mulle-objc/MulleObjC.svg) [![Build Status](https://github.com/mulle-objc/MulleObjC/workflows/CI/badge.svg)](//github.com/mulle-objc/MulleObjC/actions) | [RELEASENOTES](RELEASENOTES.md) | ## API diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1c2292a4..23291035 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,8 @@ ## 0.25.0 +* NSInvocation does not raise anymore during -init as noone should raise during init +* NSAutoreleasePool name can now be 48-1 bytes long + * added new types `NSUIntegerAtomic` and `NSIntegerAtomic` to facilitate @property code * experimental and *untested* `MulleProxy` class added * added NSAutoreleasePool debugging facility to dump contents into CSV format for postprocessing with sqlite or scripts diff --git a/cmake/share/InstallRpath.cmake b/cmake/share/InstallRpath.cmake index dbdae7fc..8e03fcb4 100644 --- a/cmake/share/InstallRpath.cmake +++ b/cmake/share/InstallRpath.cmake @@ -27,10 +27,15 @@ if( MULLE_NO_CMAKE_INSTALL_RPATH) set( CMAKE_SKIP_BUILD_RPATH ON) else() if( APPLE) - set( CMAKE_INSTALL_RPATH - "@loader_path/../lib/" - "@loader_path/../Frameworks/" - ) + if( CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) + # Modern CMake handles lib path automatically + set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") + else() + set(CMAKE_INSTALL_RPATH + "@loader_path/../lib/" + "@loader_path/../Frameworks/" + ) + endif() else() set( CMAKE_INSTALL_RPATH "\$ORIGIN/../lib") endif() diff --git a/src/MulleObjCIntegralType.h b/src/MulleObjCIntegralType.h index 93bee1de..e76d2d94 100644 --- a/src/MulleObjCIntegralType.h +++ b/src/MulleObjCIntegralType.h @@ -13,6 +13,7 @@ #include #include +#include typedef enum { diff --git a/src/class/NSAutoreleasePool.h b/src/class/NSAutoreleasePool.h index f5041e15..20dc3784 100644 --- a/src/class/NSAutoreleasePool.h +++ b/src/class/NSAutoreleasePool.h @@ -56,7 +56,7 @@ { NSAutoreleasePool *_owner; void *_storage; - char _mulleNameUTF8String[ 32]; + char _mulleNameUTF8String[ 48]; } // MEMO: this is marked as threadsafe, but that's because its assumed @@ -265,5 +265,11 @@ void MulleObjCDumpAutoreleasePoolsToFileIndexed( char *filename); MULLE_OBJC_GLOBAL void MulleObjCDumpAutoreleasePoolsToFILEWithOptions( FILE *fp, int indexed); +// +// MEMO: like the functions above, you should only use them when you +// are in the debugger and a breakpoint has happened, but for debugging +// you can sometimes get by even in running programs, if threads are +// waiting for each other. +// MULLE_OBJC_GLOBAL unsigned long MulleObjCDumpAutoreleasePoolsFrame( void); diff --git a/src/class/NSAutoreleasePool.m b/src/class/NSAutoreleasePool.m index 3f55f239..0577faae 100644 --- a/src/class/NSAutoreleasePool.m +++ b/src/class/NSAutoreleasePool.m @@ -241,7 +241,10 @@ void mulle_objc_thread_done_poolconfiguration( struct _mulle_objc_universe *un - (char *) mulleNameUTF8String { if( ! _mulleNameUTF8String[ 0]) - mulle_snprintf( _mulleNameUTF8String, sizeof( _mulleNameUTF8String), "%p", self); + mulle_snprintf( _mulleNameUTF8String, sizeof( _mulleNameUTF8String), + "", + MulleObjCInstanceGetClassNameUTF8String( self), + self); return( &_mulleNameUTF8String[ 0]); } @@ -249,6 +252,7 @@ - (char *) mulleNameUTF8String - (void) mulleSetNameUTF8String:(char *) s { strncpy( _mulleNameUTF8String, s ? s : "", sizeof( _mulleNameUTF8String) - 1); + _mulleNameUTF8String[ sizeof( _mulleNameUTF8String) - 1] = 0; } @@ -1181,7 +1185,10 @@ static inline void _dump_info_init( struct dump_info *p, { if( thread != info->thread.mainthread) { - mulle__pointermap_set( &p->thread_index_map, thread, (void *) (intptr_t) thread_index, NULL); + mulle__pointermap_set( &p->thread_index_map, + thread, + (void *) (intptr_t) thread_index, + NULL); ++thread_index; } } @@ -1190,6 +1197,9 @@ static inline void _dump_info_init( struct dump_info *p, static inline void _dump_info_done( struct dump_info *p) { + mulle_free( p->thread_name); + mulle_free( p->pool_name); + _mulle__pointermap_done( &p->thread_index_map, NULL); _mulle__pointermap_done( &p->object_index_map, NULL); } @@ -1204,7 +1214,9 @@ static void _dumpinfo_dump_thread( struct dump_info *info, struct _mulle_objc_poolconfiguration *config; info->thread_adr = thread; - info->thread_name = [thread mulleNameUTF8String]; + + mulle_free( info->thread_name); + info->thread_name = mulle_strdup( [thread mulleNameUTF8String]); info->thread_index = (int) index; info->pool_index = 0; @@ -1214,7 +1226,8 @@ static void _dumpinfo_dump_thread( struct dump_info *info, for( pool = config->tail; pool; pool = pool->_owner) { - info->pool_name = [pool mulleNameUTF8String]; + mulle_free( info->pool_name); + info->pool_name = mulle_strdup( [pool mulleNameUTF8String]); info->pool_adr = pool; --info->pool_index; @@ -1250,7 +1263,11 @@ static void _dumpinfo_dump_thread( struct dump_info *info, _dump_info_done( &dump_info); } - +// +// Though we do a "lock" its just to block another +// MulleObjCDumpAutoreleasePoolsToFILEWithOptions, dumping other threads +// autoreleasepools is obviously risky. +// void MulleObjCDumpAutoreleasePoolsToFILEWithOptions( FILE *fp, int options) { struct _mulle_objc_universe *universe; diff --git a/src/class/NSInvocation.h b/src/class/NSInvocation.h index aadd0e2f..5f5f3715 100644 --- a/src/class/NSInvocation.h +++ b/src/class/NSInvocation.h @@ -89,7 +89,7 @@ - (void) setSelector:(SEL) selector; - (id) target; -- (void) setTarget:target; +- (void) setTarget:(id) target; - (void) invoke; - (void) invokeWithTarget:(id) target; diff --git a/src/class/NSInvocation.m b/src/class/NSInvocation.m index 923dfbae..c557dbb7 100644 --- a/src/class/NSInvocation.m +++ b/src/class/NSInvocation.m @@ -174,12 +174,9 @@ + (NSInvocation *) invocationWithMethodSignature:(NSMethodSignature *) signature void *extraBytes; struct _mulle_objc_infraclass *cls; + // MEMO: do not raise during construction of objects if( ! signature) - { - __mulle_objc_universe_raise_invalidargument( _mulle_objc_object_get_universe( self), - "signature is nil"); return( nil); - } // frame_size = [signature frameLength]; // size = mulle_metaabi_sizeof_union( frame_size); @@ -231,6 +228,9 @@ + (NSInvocation *) mulleInvocationWithTarget:(id) target signature = [target methodSignatureForSelector:sel]; invocation = [self invocationWithMethodSignature:signature]; + if( ! invocation) + return( nil); + [invocation setTarget:target]; [invocation setSelector:sel]; @@ -268,13 +268,7 @@ + (NSInvocation *) mulleInvocationWithTarget:(id) target NSInvocation *invocation; NSMethodSignature *signature; - signature = [target methodSignatureForSelector:sel]; - if( ! signature) - MulleObjCThrowInternalInconsistencyExceptionUTF8String( "method not found on target"); - if( [signature numberOfArguments] != 3) - MulleObjCThrowInternalInconsistencyExceptionUTF8String( "method must accept one argument"); - if( [signature isVariadic]) - MulleObjCThrowInternalInconsistencyExceptionUTF8String( "method must not be variadic"); + signature = [target methodSignatureForSelector:sel]; invocation = [self invocationWithMethodSignature:signature]; [invocation setTarget:target]; @@ -297,11 +291,7 @@ + (NSInvocation *) mulleInvocationWithTarget:(id) target void *adr; MulleObjCMethodSignatureTypeInfo *p; #endif - signature = [target methodSignatureForSelector:sel]; - if( ! signature) - MulleObjCThrowInternalInconsistencyExceptionUTF8String( "method %x not found on target", sel); - if( [signature isVariadic]) - MulleObjCThrowInternalInconsistencyExceptionUTF8String( "method must not be variadic"); + signature = [target methodSignatureForSelector:sel]; invocation = [self invocationWithMethodSignature:signature]; [invocation setTarget:target]; @@ -780,6 +770,8 @@ - (void) invokeWithTarget:(id) target MulleObjCThrowInternalInconsistencyExceptionUTF8String( "NSInvocation: selector has not been set yet"); if( ! _methodSignature) MulleObjCThrowInternalInconsistencyExceptionUTF8String( "NSInvocation: methodSignature has not been set yet"); + if( [_methodSignature isVariadic]) + MulleObjCThrowInternalInconsistencyExceptionUTF8String( "method must not be variadic"); pType = [_methodSignature _methodMetaABIParameterType]; rType = [_methodSignature _methodMetaABIReturnType]; diff --git a/src/class/NSObject.m b/src/class/NSObject.m index 9b6d02eb..50649acf 100644 --- a/src/class/NSObject.m +++ b/src/class/NSObject.m @@ -121,6 +121,11 @@ - (void *) forward:(void *) _param MULLE_OBJC_THREADSAFE_METHOD } #endif + // MEMO: can't autorelease ahead of init, because init can return + // nil and release object, or release object and return another + // alloced one. So if -init raises, we leak and can't do much + // about. -raise during -init == BAD + // obj = [_cls alloc]; obj = mulle_objc_object_call_variable_inline( obj, (mulle_objc_methodid_t) _cmd, diff --git a/src/class/NSThread.m b/src/class/NSThread.m index 8780a561..2d6f2bde 100644 --- a/src/class/NSThread.m +++ b/src/class/NSThread.m @@ -946,6 +946,8 @@ - (id) mulleSetRunLoop:(id) runLoop id otherRunloop; assert( ! runLoop || [runLoop mulleIsAccessibleByThread:self]); + assert( ! _mulle_objc_object_is_finalized( (struct _mulle_objc_object *) self)); + [runLoop retain]; otherRunloop = __mulle_atomic_pointer_compare_and_swap( &self->_runLoop, runLoop, NULL); if( otherRunloop) @@ -1107,7 +1109,9 @@ - (char *) mulleNameUTF8String s = _mulle_atomic_pointer_read( &_nameUTF8String); if( ! s) - s = MulleObjC_asprintf( "%p", self); + s = MulleObjC_asprintf( "<%s %p>", + MulleObjCInstanceGetClassNameUTF8String( self), + self); return( s); } diff --git a/src/reflect/_MulleObjC-versioncheck.h b/src/reflect/_MulleObjC-versioncheck.h index b0f2ba82..d5e06485 100644 --- a/src/reflect/_MulleObjC-versioncheck.h +++ b/src/reflect/_MulleObjC-versioncheck.h @@ -7,7 +7,7 @@ #if defined( MULLE__OBJC__DEBUG_VERSION) # ifndef MULLE__OBJC__DEBUG_VERSION_MIN -# define MULLE__OBJC__DEBUG_VERSION_MIN ((0UL << 20) | (21 << 8) | 2) +# define MULLE__OBJC__DEBUG_VERSION_MIN ((0UL << 20) | (21 << 8) | 3) # endif # ifndef MULLE__OBJC__DEBUG_VERSION_MAX # define MULLE__OBJC__DEBUG_VERSION_MAX ((0UL << 20) | (22 << 8) | 0) @@ -22,10 +22,10 @@ #if defined( MULLE__OBJC__RUNTIME_VERSION) # ifndef MULLE__OBJC__RUNTIME_VERSION_MIN -# define MULLE__OBJC__RUNTIME_VERSION_MIN ((0UL << 20) | (24 << 8) | 0) +# define MULLE__OBJC__RUNTIME_VERSION_MIN ((0UL << 20) | (25 << 8) | 0) # endif # ifndef MULLE__OBJC__RUNTIME_VERSION_MAX -# define MULLE__OBJC__RUNTIME_VERSION_MAX ((0UL << 20) | (25 << 8) | 0) +# define MULLE__OBJC__RUNTIME_VERSION_MAX ((0UL << 20) | (26 << 8) | 0) # endif # if MULLE__OBJC__RUNTIME_VERSION < MULLE__OBJC__RUNTIME_VERSION_MIN # error "mulle-objc-runtime is too old" diff --git a/test/.mulle/share/env/environment-plugin.sh b/test/.mulle/share/env/environment-plugin.sh index e35d3a2f..33b0fbb6 100644 --- a/test/.mulle/share/env/environment-plugin.sh +++ b/test/.mulle/share/env/environment-plugin.sh @@ -22,6 +22,6 @@ export MULLE_SOURCETREE_SYMLINK='YES' # # # -export MULLE_SDE_INSTALLED_VERSION="3.2.0" +export MULLE_SDE_INSTALLED_VERSION="3.2.2" diff --git a/test/.mulle/share/env/version b/test/.mulle/share/env/version index 03f488b0..c7cb1311 100644 --- a/test/.mulle/share/env/version +++ b/test/.mulle/share/env/version @@ -1 +1 @@ -5.3.0 +5.3.1 diff --git a/test/NSAutoreleasePool/pooldump.m b/test/NSAutoreleasePool/pooldump.m index cd2e223a..a8e24d35 100644 --- a/test/NSAutoreleasePool/pooldump.m +++ b/test/NSAutoreleasePool/pooldump.m @@ -46,7 +46,7 @@ + (instancetype) objectWithNameUTF8String:(char *) s - (char *) mulleNameUTF8String { if( ! _mulleNameUTF8String[ 0]) - mulle_snprintf( _mulleNameUTF8String, sizeof( _mulleNameUTF8String), "%p", self); + mulle_snprintf( _mulleNameUTF8String, sizeof( _mulleNameUTF8String), "", self); return( &_mulleNameUTF8String[ 0]); } diff --git a/test/TAO/forward/TAOStrategyTest.inc b/test/TAO/forward/TAOStrategyTest.inc index dd7392cb..857e596d 100644 --- a/test/TAO/forward/TAOStrategyTest.inc +++ b/test/TAO/forward/TAOStrategyTest.inc @@ -33,8 +33,24 @@ static void dump_trace_text( unsigned long nr, char *format, va_list args) } mulle_vfprintf( fp, format, args); - mulle_fprintf( fp, "\n"); - _mulle_stacktrace( NULL, 2, mulle_stacktrace_linefeed, fp); + fclose( fp); +} + + +static void dump_trace_stack( unsigned long nr) +{ + auto char buf[ 19 + 32 + 4 + 1]; + FILE *fp; + + mulle_snprintf( buf, sizeof( buf), "NSAutoreleasePools_%06d.trc", nr); + fp = fopen( buf, "w"); + if( ! fp) + { + perror( "fopen:"); + return; + } + + _mulle_stacktrace( NULL, 3, mulle_stacktrace_csv, fp); fclose( fp); } @@ -55,6 +71,7 @@ static void test_trace( char *format, ...) nr = MulleObjCDumpAutoreleasePoolsFrame(); va_start( args, format); dump_trace_text( nr, format, args); + dump_trace_stack( nr); va_end( args); } #endif diff --git a/test/TAO/forward/generate-animation.sh b/test/TAO/forward/generate-animation.sh index f687b6ea..5e54b0f0 100755 --- a/test/TAO/forward/generate-animation.sh +++ b/test/TAO/forward/generate-animation.sh @@ -6,6 +6,65 @@ if [ "$#" -lt 1 ]; then exit 1 fi + +output_stacktrace() +{ + log_entry "output_stacktrace" "$@" + + local address + local segment_offset + local symbol_offset + local symbol_address + local symbol_name + local segment_address + local segment_name + + local i + local line + local boring_line + local function_name + local location + + local source_file + local line_number + + # Skip the header line + + while IFS=',' read -r address segment_offset symbol_offset symbol_address symbol_name segment_address segment_name + do + if [ "${address}" = 'address' ] + then + continue + fi + + segment_name="${segment_name//\"/}" + segment_offset="${segment_offset//0x/}" + + # ignore runtime code + case "${segment_name}" in + *mulle-objc-runtime.*) + continue + ;; + esac + + i=0 + while IFS= read -r line + do + case $i in + 0) boring_line="$line" ;; + 1) function_name="$line" ;; + 2) location="$line" ;; + esac + ((i++)) + done < <(rexekutor addr2line -f -e "${segment_name}" -a "${segment_offset}") + + IFS=':' read -r source_file line_number <<< "${location}" + + printf "%s:%s %s\n" "${source_file#${MULLE_USER_PWD}/}" "${line_number:0}" "${function_name}" + done +} + + output_html() { log_entry "output_html" "$@" @@ -75,16 +134,19 @@ EOF # Add number divs echo " const titles = [" >> animation.html - for svg in "$@"; do + for svg in "$@" + do echo " '${svg%.svg}'," >> animation.html done echo " ];" >> animation.html # Add title divs echo " const commands = [" >> animation.html - for svg in "$@"; do + for svg in "$@" + do txt_file="${svg%.svg}.txt" - if [ -f "$txt_file" ]; then + if [ -f "$txt_file" ] + then title=$(head -n1 "$txt_file") echo " '$title'," >> animation.html else @@ -96,9 +158,10 @@ EOF # Add stack traces echo " const traces = [" >> animation.html for svg in "$@"; do - txt_file="${svg%.svg}.txt" - if [ -f "$txt_file" ]; then - trace=$(tail -n +2 "$txt_file" | sed 's/"/\\"/g' | tr '\n' '\\' | sed 's/\\/\\n/g') + trc_file="${svg%.svg}.trc" + if [ -f "$trc_file" ] + then + trace=$(output_stacktrace < "$trc_file" | sed 's/"/\\"/g' | tr '\n' '\\' | sed 's/\\/\\n/g') echo " '$trace'," >> animation.html else echo " ''," >> animation.html @@ -161,6 +224,18 @@ EOF } +create_svg_file() +{ + log_entry "create_svg_file" "$@" + + local csv="$1" + local dot="$2" + local svg="$3" + + pooldump-dot.awk "${csv}" > "${dot}" \ + && dot -Tsvg -o "${svg}" "${dot}" +} + output() { @@ -180,17 +255,18 @@ output() dot="${RVAL}.dot" svg="${RVAL}.svg" - pooldump-dot.awk "${csv}" > "${dot}" || exit 1 - dot -Tsvg -o "${svg}" "${dot}" + create_svg_file "${csv}" "${dot}" "${svg}" & svg_files+=("${svg}") done + wait + output_html "${svg_files[@]}" } - -PATH="${PATH}:${PWD}" +r_dirname "${MULLE_EXECUTABLE}" +PATH="${PATH}:${RVAL}" output "$@" diff --git a/test/TAO/forward/pooldump-dot.awk b/test/TAO/forward/pooldump-dot.awk index e866155b..b73482b3 100755 --- a/test/TAO/forward/pooldump-dot.awk +++ b/test/TAO/forward/pooldump-dot.awk @@ -28,6 +28,16 @@ NR == 2 { num_fields = NF } +function html_escape( str) { + gsub(/&/, "\\&", str) + gsub(//, "\\>", str) + gsub(/"/, "\\"", str) + gsub(/'/, "\\'", str) + return str +} + + NR > 1 { gsub(/"/, "", $2) # thread name gsub(/"/, "", $4) # pool name @@ -35,12 +45,12 @@ NR > 1 { gsub(/"/, "", $7) # class name thread_id = $1 - thread_name = $2 + thread_name = html_escape( $2) pool_id = $3 - pool_name = $4 + pool_name = html_escape( $4) obj_id = $5 - obj_name = $6 - class_name = $7 + obj_name = html_escape( $6) + class_name = html_escape( $7) rc = $8 if (num_fields >= 10) {