From aa6b109c8329ab1dbea9b23a0cf3d10700c28b70 Mon Sep 17 00:00:00 2001 From: "Fred T. Hamster" Date: Mon, 9 Feb 2026 23:25:36 -0500 Subject: [PATCH] breaking change with callstack tracker enabled this doesn't compile right now, but is getting closer. currently trying to resolve the problems in buildor_gen_deps, but may have just found the smoking gun. or bug. bad bug. --- .../library/application/callstack_tracker.cpp | 41 ++++++++++++++--- .../library/application/callstack_tracker.h | 6 +++ .../structures/static_memory_gremlin.cpp | 5 ++- .../test_path_configuration.cpp | 2 +- nucleus/library/tests_basis/test_mutex.cpp | 44 ++++++++++++++++--- nucleus/library/tests_basis/test_string.cpp | 7 +-- nucleus/tools/clam_tools/value_tagger.cpp | 2 +- production/feisty_meow_config.ini | 2 +- scripts/clam/cpp/buildor_gen_deps.sh | 44 ++++++++++--------- 9 files changed, 112 insertions(+), 41 deletions(-) diff --git a/nucleus/library/application/callstack_tracker.cpp b/nucleus/library/application/callstack_tracker.cpp index 5f57dd36..00e5a432 100644 --- a/nucleus/library/application/callstack_tracker.cpp +++ b/nucleus/library/application/callstack_tracker.cpp @@ -54,23 +54,42 @@ const char *emptiness_note = "Empty Stack\n"; ////////////// +basis::mutex &callstack_tracker::__callstack_tracker_synchronizer() +{ + static basis::mutex __global_synch_callstacks; + return __global_synch_callstacks; +} + +////////////// + //! the single instance of callstack_tracker. -/*! this is also an ultra low-level object, although it's not as far down +/*! + this is also an ultra low-level object, although it's not as far down as the memory checker. it can allocate c++ objects and that kind of thing just fine. the object must be stored here rather than in the static basis library due to issues in windows dlls. -NOTE: this is also not thread safe; it must be initialized before any threads -have started. */ + NOTE: the construction process for this is not thread-safe; the static +program-wide object must be initialized before any threads have started. +that is normally done in... +uhhh.... + +beuller? +... + -//hmmm: why is this here? because it needs to interact with the progwide memories? +*/ callstack_tracker &program_wide_stack_trace() { + auto_synchronizer l(callstack_tracker::__callstack_tracker_synchronizer()); + static callstack_tracker *_hidden_trace = NULL_POINTER; if (!_hidden_trace) { #ifdef ENABLE_MEMORY_HOOK program_wide_memories().disable(); - // we don't want infinite loops tracking the call stack during this - // object's construction. + /* we don't want infinite loops tracking the call stack during this object's construction. */ +//hmmm: does that disable the progwide memories for the whole program or just for this thread? +// and what does that entail exactly? +// did it actually fix the problem we saw? #endif _hidden_trace = new callstack_tracker; #ifdef ENABLE_MEMORY_HOOK @@ -119,6 +138,8 @@ callstack_tracker::~callstack_tracker() bool callstack_tracker::push_frame(const char *class_name, const char *func, const char *file, int line) { + auto_synchronizer l(callstack_tracker::__callstack_tracker_synchronizer()); + //printf("callstack pushframe depth=%d in\n", _depth); if (_unusable) return false; if (_depth >= MAX_STACK_DEPTH) { @@ -137,6 +158,8 @@ bool callstack_tracker::push_frame(const char *class_name, const char *func, bool callstack_tracker::pop_frame() { + auto_synchronizer l(callstack_tracker::__callstack_tracker_synchronizer()); + //printf("callstack popframe depth=%d in\n", _depth); if (_unusable) return false; if (_depth <= 0) { @@ -154,6 +177,8 @@ bool callstack_tracker::pop_frame() bool callstack_tracker::update_line(int line) { + auto_synchronizer l(callstack_tracker::__callstack_tracker_synchronizer()); + if (_unusable) return false; if (!_depth) return false; // not as serious, but pretty weird. _bt->_records[_depth]._line = line; @@ -162,6 +187,8 @@ bool callstack_tracker::update_line(int line) char *callstack_tracker::full_trace() const { + auto_synchronizer l(callstack_tracker::__callstack_tracker_synchronizer()); + if (_unusable) return strdup(""); //printf("fulltrace in\n"); char *to_return = (char *)malloc(full_trace_size()); @@ -212,6 +239,8 @@ char *callstack_tracker::full_trace() const int callstack_tracker::full_trace_size() const { + auto_synchronizer l(callstack_tracker::__callstack_tracker_synchronizer()); + if (_unusable) return 0; if (!_depth) return strlen(emptiness_note) + 14; // liberal allocation. int to_return = 28; // another hollywood style excess. diff --git a/nucleus/library/application/callstack_tracker.h b/nucleus/library/application/callstack_tracker.h index a8e83169..e6392e3f 100644 --- a/nucleus/library/application/callstack_tracker.h +++ b/nucleus/library/application/callstack_tracker.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace application { @@ -32,6 +33,8 @@ class callstack_tracker; callstack_tracker &program_wide_stack_trace(); //!< a global object that can be used to track the runtime callstack. +//hmmm: maybe borked on basis of the conflict between a global stack tracker and the fact that each thread has its own callstack! argh! + ////////////// //! This object can provide a backtrace at runtime of the invoking methods. @@ -87,6 +90,9 @@ public: double highest() const { return _highest; } //!< reports the maximum stack depth seen during the runtime so far. + static basis::mutex &__callstack_tracker_synchronizer(); + //!< protects concurrent access. + private: callstack_records *_bt; //!< the backtrace records for current program. int _depth; //!< the current number of frames we know of. diff --git a/nucleus/library/structures/static_memory_gremlin.cpp b/nucleus/library/structures/static_memory_gremlin.cpp index fc95d75f..88ba32d4 100644 --- a/nucleus/library/structures/static_memory_gremlin.cpp +++ b/nucleus/library/structures/static_memory_gremlin.cpp @@ -142,8 +142,8 @@ bool static_memory_gremlin::__program_is_dying() { return __global_program_is_dy mutex &static_memory_gremlin::__memory_gremlin_synchronizer() { - static mutex __globabl_synch_mem; - return __globabl_synch_mem; + static mutex __global_synch_mem; + return __global_synch_mem; } int static_memory_gremlin::locate(const char *unique_name) @@ -256,6 +256,7 @@ static_memory_gremlin &static_memory_gremlin::__hoople_globals() application::program_wide_stack_trace().full_trace_size(); // invoke now to get callback tracking instantiated. #endif + FUNCDEF("HOOPLE_GLOBALS remainder"); // this definition must be postponed until after the objects that would // track it actually exist. diff --git a/nucleus/library/tests_application/test_path_configuration.cpp b/nucleus/library/tests_application/test_path_configuration.cpp index a0ce8c9a..0ab750b5 100644 --- a/nucleus/library/tests_application/test_path_configuration.cpp +++ b/nucleus/library/tests_application/test_path_configuration.cpp @@ -34,7 +34,7 @@ using namespace loggers; #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) //hmmm: ugly old main() without using the hoople machinery. ack. -astring static_class_name() { return "test_path_configuration"; } +const char *static_class_name() { return "test_path_configuration"; } HOOPLE_STARTUP_CODE; diff --git a/nucleus/library/tests_basis/test_mutex.cpp b/nucleus/library/tests_basis/test_mutex.cpp index 86175c4e..1783de12 100644 --- a/nucleus/library/tests_basis/test_mutex.cpp +++ b/nucleus/library/tests_basis/test_mutex.cpp @@ -41,7 +41,7 @@ using namespace processes; using namespace structures; using namespace unit_test; -//#define DEBUG_MUTEX +#define DEBUG_MUTEX // uncomment for a verbose test run. const int MAX_MUTEX_TIMING_TEST = 2000000; @@ -214,6 +214,10 @@ int test_mutex::execute() { FUNCDEF("execute"); +#ifdef DEBUG_MUTEX + LOG("entering execute method."); +#endif + // make sure the guard is initialized before the threads run. guard().lock(); guard().unlock(); @@ -236,21 +240,47 @@ int test_mutex::execute() run_count, full_run_time)); log(a_sprintf("or %f ms per (lock+unlock).", time_per_lock)); ASSERT_TRUE(time_per_lock < 1.0, "mutex lock timing should be super fast"); +#ifdef DEBUG_MUTEX + LOG("about to exit scope and dump automatic objects."); +#endif } +#ifdef DEBUG_MUTEX + LOG("succeeded in exiting scope."); +#endif + amorph thread_list; for (int i = 0; i < DEFAULT_FISH; i++) { ethread *t = NULL_POINTER; - if (i % 2) t = new piranha(*this); - else t = new barracuda(*this); + if (i % 2) { + t = new piranha(*this); +#ifdef DEBUG_MUTEX + LOG(a_sprintf("indy %i: adding new piranha now.", i)); +#endif + } else { + t = new barracuda(*this); +#ifdef DEBUG_MUTEX + LOG(a_sprintf("indy %i: adding new piranha now.", i)); +#endif + } thread_list.append(t); ethread *q = thread_list[thread_list.elements() - 1]; ASSERT_EQUAL(q, t, "amorph pointer equivalence is required"); +#ifdef DEBUG_MUTEX + LOG(a_sprintf("indy %i: about to start new thread.", i)); +#endif // start the thread we added. q->start(NULL_POINTER); +#ifdef DEBUG_MUTEX + LOG(a_sprintf("indy %i: after new thread started.", i)); +#endif } +#ifdef DEBUG_MUTEX + LOG("about to begin snoozing."); +#endif + time_stamp when_to_leave(DEFAULT_RUN_TIME); while (when_to_leave > time_stamp()) { time_control::sleep_ms(100); @@ -260,13 +290,13 @@ int test_mutex::execute() // that should work fine. #ifdef DEBUG_MUTEX - LOG("now cancelling all threads...."); + LOG("now cancelling all threads."); #endif for (int j = 0; j < thread_list.elements(); j++) thread_list[j]->cancel(); #ifdef DEBUG_MUTEX - LOG("now stopping all threads...."); + LOG("now stopping all threads."); #endif for (int k = 0; k < thread_list.elements(); k++) thread_list[k]->stop(); @@ -278,7 +308,7 @@ int test_mutex::execute() ASSERT_EQUAL(threads_active, 0, "threads should actually have stopped by now"); #ifdef DEBUG_MUTEX - LOG("resetting thread list...."); + LOG("resetting thread list."); #endif thread_list.reset(); // should whack all threads. @@ -286,7 +316,7 @@ int test_mutex::execute() ASSERT_EQUAL(concurrent_biters, 0, "threads should all be gone by now"); #ifdef DEBUG_MUTEX - LOG("done exiting from all threads...."); + LOG("done exiting from all threads."); LOG(astring(astring::SPRINTF, "the accumulated string had %d characters " "which means\nthere were %d thread activations from %d threads.", diff --git a/nucleus/library/tests_basis/test_string.cpp b/nucleus/library/tests_basis/test_string.cpp index 0eddf073..922acc3e 100644 --- a/nucleus/library/tests_basis/test_string.cpp +++ b/nucleus/library/tests_basis/test_string.cpp @@ -49,8 +49,6 @@ using namespace textual; using namespace timely; using namespace unit_test; -//HOOPLE_STARTUP_CODE; - //#define DEBUG_STRING_TEST // uncomment for testing version. @@ -859,6 +857,8 @@ void test_string::run_test_29() ASSERT_EQUAL(b, a, "second comparison failed"); } +#define static_class_name() "test_string" + void standard_sprintf_test(const char *parm_string) { FUNCDEF("standard_sprintf_test"); @@ -875,6 +875,8 @@ void standard_sprintf_test(const char *parm_string) parm_string, parm_string, basis::un_long(rando.inclusive(0, 2998238))); } +#undef static_class_name + void test_string::run_test_30() { // 30th test group checks astring sprintf. @@ -1278,4 +1280,3 @@ int test_string::execute() HOOPLE_MAIN(test_string, ) - diff --git a/nucleus/tools/clam_tools/value_tagger.cpp b/nucleus/tools/clam_tools/value_tagger.cpp index 90b6a053..bd90b910 100644 --- a/nucleus/tools/clam_tools/value_tagger.cpp +++ b/nucleus/tools/clam_tools/value_tagger.cpp @@ -24,6 +24,7 @@ * Please send any updates to: fred@gruntose.com * \*****************************************************************************/ +#include #include #include #include @@ -46,7 +47,6 @@ #include -#include "../../library/algorithms/sorts.h" #ifdef __WIN32__ #include #endif diff --git a/production/feisty_meow_config.ini b/production/feisty_meow_config.ini index b4591ed4..27606287 100644 --- a/production/feisty_meow_config.ini +++ b/production/feisty_meow_config.ini @@ -84,6 +84,6 @@ endif # methods with a way to know how they were invoked. to get this information. # the invoker must be fitted with FUNCDEF macros. ifeq "$(BOOT_STRAPPING)" "" -# DEFINITIONS += ENABLE_CALLSTACK_TRACKING + DEFINITIONS += ENABLE_CALLSTACK_TRACKING endif diff --git a/scripts/clam/cpp/buildor_gen_deps.sh b/scripts/clam/cpp/buildor_gen_deps.sh index e865bc26..8b463962 100644 --- a/scripts/clam/cpp/buildor_gen_deps.sh +++ b/scripts/clam/cpp/buildor_gen_deps.sh @@ -62,15 +62,15 @@ function add_new_dep { # make sure we haven't already processed this. local dep="$1" if seen_already "$dep"; then -#echo bailing since seen: $dep +echo bailing since seen: $dep return 1 fi -#echo had not seen before: $dep +echo had not seen before: $dep # if existing_dep $dep; then return 1; fi # added it to list already. # if bad_file $dep; then return 1; fi # known to suck. # if boring_file $dep; then return 1; fi # we already saw it. -##echo new dep: $dep +echo new dep: $dep dependency_accumulator+=($dep) return 0 @@ -166,39 +166,39 @@ declare -a resolve_matches_dest=() # tries to find a filename in the library hierarchy. function resolve_filename { local code_file=$1 -#echo resolving: $code_file +echo "resolving: $code_file" if [ -f "$code_file" ]; then # that was pretty easy. resolve_target_array=($code_file) return 0 fi -#echo "MUST seek: $code_file" +echo "MUST seek: $code_file" local dir=$(dirname "$code_file") local base=$(basename "$code_file") local src_key="$dir/$base" -#echo "src_key: $src_key" +echo "src_key: $src_key" # see if we can find that element in the previously resolved items. if find_in_array "$src_key" ${resolve_matches_src[*]}; then local found_indy=$__finders_indy resolve_target_array=(${resolve_matches_dest[$found_indy]}) -#echo "FOUND \"$src_key\" AT ${resolve_matches_dest[$found_indy]}" +echo "FOUND \"$src_key\" AT ${resolve_matches_dest[$found_indy]}" return 0 fi # reset our global list. resolve_target_array=() -#echo "HAVING TO FIND: $dir and $base" +echo "HAVING TO FIND: $dir and $base" if [ -z "$dir" ]; then resolve_target_array=($(find "$BUILD_TOP" -iname "$base")) else resolve_target_array=($(find "$BUILD_TOP" -iname "$base" | grep "$dir.$base")) fi -#echo resolved to: ${resolve_target_array[*]} -#echo size of resolve array=${#resolve_target_array[*]} +echo resolved to: ${resolve_target_array[*]} +echo size of resolve array=${#resolve_target_array[*]} if [ ${#resolve_target_array[*]} -eq 1 ]; then -#echo ADDING a match: $src_key ${resolve_target_array[0]} +echo ADDING a match: $src_key ${resolve_target_array[0]} # for unique matches, we will store the correspondence so we can look # it up very quickly later. resolve_matches_src+=($src_key) @@ -261,7 +261,7 @@ function recurse_on_deps { #hold #rm "$partial_file" -#echo "grabbing includes from: $to_examine" +echo "grabbing includes from: $to_examine" #hmmm: could separate the find deps on this file stuff below. @@ -272,8 +272,9 @@ function recurse_on_deps { # we haven't already. while read -r line_found; do local chew_toy=$(echo $line_found | sed -e 's/^[ \t]*#include *<\(.*\)>.*$/\1/') + local original_value="$chew_toy" # we want to add the file to the active list before we forgot about it. -#echo A: chew_toy=$chew_toy +echo A: chew_toy=$chew_toy # check whether the dependency looks like one of our style of includes. # if it doesn't have a slash in it, then we need to give it the same @@ -285,7 +286,7 @@ function recurse_on_deps { if [ ! -z "$(echo $chew_toy | sed -n -e 's/#include/crud/p')" ]; then # try again with a simpler pattern. chew_toy=$(echo $line_found | sed -e 's/^[ \t]*#include *[">]\(.*\)[">].*$/\1/') -#echo B: chew_toy=$chew_toy +echo B: chew_toy=$chew_toy # if it still has an #include or if it's not really a file, we can't # use it for anything. @@ -304,13 +305,13 @@ function recurse_on_deps { else # cool, we can rely on the existing directory. chew_toy="$fp_dir/$chew_toy" -#echo patched dir: $chew_toy +echo patched dir: $chew_toy fi fi fi if bad_file $chew_toy; then -#echo C: skipping because on bad list: $chew_toy +echo C: skipping because on bad list: $chew_toy continue fi @@ -331,13 +332,13 @@ function recurse_on_deps { #echo odd len is $odd_len if [ $odd_len -eq 0 ]; then # whoops. we couldn't find it. probably a system header, so toss it. -#echo "** ignoring: $chew_toy" +echo "** ignoring: $chew_toy" bad_files+=($chew_toy) chew_toy="" elif [ $odd_len -eq 1 ]; then # there's exactly one match, which is very good. chew_toy="${found_odd[0]}" -#echo C: chew_toy=$chew_toy +echo "C: chew_toy=$chew_toy" else # this is really wrong. there are multiple files with the same name? # that kind of things makes debugger tools angry or stupid. @@ -365,12 +366,15 @@ function recurse_on_deps { active_deps+=($chew_toy) fi fi + else + echo "** chew_toy was empty! original value was '$original_value'" fi # now compute the path as if it was the implementation file (x.cpp) # instead of being a header. does that file exist? if so, we'd like # its dependencies also. - local cpp_toy=$(echo $chew_toy | sed -e 's/^\([^\.]*\)\.h$/\1.cpp/') + local cpp_toy=$(echo -n $chew_toy | sed -e 's/^\([^\.]*\)\.h$/\1.cpp/') +echo "cpp_toy is '$cpp_toy' as derived from chew_toy '$chew_toy'" # there's no point in adding it if the name didn't change. if [ "$cpp_toy" != "$chew_toy" ]; then @@ -462,8 +466,8 @@ echo "skipping header file: $chewed_line" fi local new_include=" #include <$chewed_line>" - echo "$new_include" >>"$pending_deps" echo "adding '$new_include'" + echo "$new_include" >>"$pending_deps" done # check that our dependencies file is not empty still. -- 2.34.1