From d1477952f1df101b7d537db373c06995772e15b1 Mon Sep 17 00:00:00 2001 From: niry1 Date: Mon, 1 Jun 2026 16:51:34 +0300 Subject: [PATCH] iIssue #401 - Document the interactions between READSTOP and context - Also gdb improvements --- manpage | 26 ++- specs/docs/alu.md | 2 +- specs/docs/streams.md | 8 +- specs/src/gdb/specs.gdb | 23 ++- specs/src/gdb/specs_gdb.py | 377 +++++++++++++++++++++++-------------- 5 files changed, 274 insertions(+), 162 deletions(-) diff --git a/manpage b/manpage index 491b163..66fb5ef 100644 --- a/manpage +++ b/manpage @@ -228,9 +228,17 @@ Convert printable time format to a number, representing microseconds since the e .SS "Other Spec Units" There are also other spec units, that may be used: .IP "READ" 3 -Causes the program to read the next line of input. If we have already read the last line, the read line is taken to be the empty string. +Causes the program to read the next line of input. If we have already read the last line, the read line is taken to be the empty string. When a +.B READ +or +.B READSTOP +spec unit is applied, the context offset is reset to zero (the current record). .IP "READSTOP" 3 -Causes the program to read the next line of input. If we have already read the last line, no more processing is done for this iteration. +Causes the program to read the next line of input. If we have already read the last line, no more processing is done for this iteration. When a +.B READ +or +.B READSTOP +spec unit is applied, the context offset is reset to zero (the current record). .IP "UNREAD" 3 Causes the program to push back the current active record back to the reader, so that the next iteration of the specification or the next .I READ @@ -367,7 +375,11 @@ resets the working string to the current record, sets it to the next record, .B CONTEXT -1 sets it to the previous record, and so on. -The required buffer sizes are computed at parse time from the largest positive and negative offsets used. If the offset refers to a record beyond the beginning or end of the input, the working string is set to the empty string. +The required buffer sizes are computed at parse time from the largest positive and negative offsets used. If the offset refers to a record beyond the beginning or end of the input, the working string is set to the empty string. Note that reading beyond the input with +.B CONTEXT +does not cause processing to stop, even if a +.B READSTOP +token is present in the specification. .P .B CONTEXT must not be abbreviated. It is not supported with threading or multiple input streams. @@ -399,6 +411,14 @@ Example: echo -e "A\\nB\\nC" | specs print @+1 1 .P produces: "B", "C", "" -- each line shows the next record (empty for the last). +.P +Note that reading beyond the input with +.B @+n +or +.B @-n +does not cause processing to stop, even if a +.B READSTOP +token is present in the specification. .SS "MainOptions" These are optional spec units that appear at the beginning of the specification and modify the behavior of the entire specification. diff --git a/specs/docs/alu.md b/specs/docs/alu.md index f88d765..222a985 100644 --- a/specs/docs/alu.md +++ b/specs/docs/alu.md @@ -117,7 +117,7 @@ POSIX (darwin) system using the g++ compiler and Python 3.9.6 - release variatio ``` Others are `@cols`, which contains the number of columns in the terminal screen, and `@rows`, which contains the number of rows on that same screen. -Additionally, the `@@` string stands for the entire input record. When rolling context is in effect (see [Streams and Records](streams.md#rolling-context)), `@@` always refers to the original input record. The `@!` string refers to the current record as affected by `CONTEXT`, which is the same as `@@` when no `CONTEXT` is active. The `@-n` and `@+n` syntax is an alternative to using that is effective within expressions. The following three specifications are equivalent: +Additionally, the `@@` string stands for the entire input record. When rolling context is in effect (see [Streams and Records](streams.md#rolling-context)), `@@` always refers to the original input record. The `@!` string refers to the current record as affected by `CONTEXT`, which is the same as `@@` when no `CONTEXT` is active. The `@-n` and `@+n` syntax is an alternative to using that is effective within expressions. Note that reading beyond the input with `@+n` or `@-n` does not cause processing to stop, even if a `READSTOP` token is present in the specification. The following three specifications are equivalent: ``` # Using @@ syntax # Using the CONTEXT keyword # No expression - just data fields diff --git a/specs/docs/streams.md b/specs/docs/streams.md index f4ae33e..856c7ad 100644 --- a/specs/docs/streams.md +++ b/specs/docs/streams.md @@ -36,7 +36,7 @@ So why do we have data fields at all if we don't want to output them? There can ## >1 Input Records in Each Iteration Sometimes we would like to use more than one input record to produce our output record. We use the `READ` or `READSTOP` keywords for that. -Both `READ` and `READSTOP` read the next record from the input stream to be the new active input record. The difference is what to do if the current line was the last. With `READ` the specification continues to be executed as if we have just read an empty record. With `READSTOP` the execution of the specification stops. +Both `READ` and `READSTOP` read the next record from the input stream to be the new active input record. The difference is what to do if the current line was the last. With `READ` the specification continues to be executed as if we have just read an empty record. With `READSTOP` the execution of the specification stops. When a `READ` or `READSTOP` spec unit is applied, the context offset is reset to zero (the current record). Below is an example of a specification that handles git log. A git log looks something like this: ``` @@ -250,7 +250,7 @@ A few things to note: ## Rolling Context The `SELECT SECOND` mechanism described above lets us peek one record ahead. But what if we need to look further ahead, or look *behind* at records we've already seen? The `CONTEXT` spec unit provides a general way to do this. -`CONTEXT` takes a single integer argument -- a positive number to look forward, a negative number to look backward, or zero to reset to the current record. When **specs** encounters a `CONTEXT` spec unit, it changes the active input record to the one at the given offset from the current record. Any input parts that follow will read from that record instead of the current one. +`CONTEXT` takes a single integer argument -- a positive number to look forward, a negative number to look backward, or zero to reset to the current record. When **specs** encounters a `CONTEXT` spec unit, it changes the active input record to the one at the given offset from the current record. Any input parts that follow will read from that record instead of the current one. Note that reading beyond the input with `CONTEXT` does not cause processing to stop, even if a `READSTOP` token is present in the specification. Consider the following input: ``` @@ -294,12 +294,14 @@ gamma ``` The first column comes from `WORD 1` while the *next* record is selected, and the second column comes from `WORD 1` after `CONTEXT 0` resets back to the current record. +Note that when a `READ` or `READSTOP` spec unit is applied, the context offset is automatically reset to zero (the current record). This means that any context offset set by a `CONTEXT` spec unit will be lost when `READ` or `READSTOP` is executed. + ### Context in Expressions In addition to the `CONTEXT` spec unit, **specs** supports the `@+n` and `@-n` syntax in expressions, where *n* is a non-negative integer. These evaluate to the full content of the record at the given offset: ``` specs PRINT "length(@+1)" 1 ``` -Given the input `AB`, `CDE`, `F`, this outputs `3`, `1`, `0` -- the length of the *next* record in each cycle. +Given the input `AB`, `CDE`, `F`, this outputs `3`, `1`, `0` -- the length of the *next* record in each cycle. Note that reading beyond the input with `@+n` or `@-n` does not cause processing to stop, even if a `READSTOP` token is present in the specification. Note that `@@` (the current input record) and `@+0` or `@-0` are not quite the same thing when `CONTEXT` is also used: `@@` always returns the real input record, regardless of any `CONTEXT` that may be in effect. To get the context-affected record in an expression, use `@!`: ``` diff --git a/specs/src/gdb/specs.gdb b/specs/src/gdb/specs.gdb index b124feb..07468a2 100644 --- a/specs/src/gdb/specs.gdb +++ b/specs/src/gdb/specs.gdb @@ -65,39 +65,39 @@ define dump_item end define dump_data_field - dump-data-field $arg0 + dump-data-field ((DataField)($arg0)) end define dump_token_item - dump-token-item $arg0 + dump-token-item ((TokenItem)($arg0)) end define dump_set_item - dump-set-item $arg0 + dump-set-item ((SetItem)($arg0)) end define dump_skip_item - dump-skip-item $arg0 + dump-skip-item ((SkipItem)($arg0)) end define dump_condition_item - dump-condition-item $arg0 + dump-condition-item ((ConditionItem)($arg0)) end define dump_break_item - dump-break-item $arg0 + dump-break-item ((BreakItem)($arg0)) end define dump_context_item - dump-context-item $arg0 + dump-context-item ((ContextItem)($arg0)) end define dump_select_item - dump-select-item $arg0 + dump-select-item ((SelectItem)($arg0)) end define dump_split_item - dump-split-item $arg0 + dump-split-item ((SplitItem)($arg0)) end # itemGroup @@ -156,6 +156,10 @@ define dump_freq_map dump-frequency-map $arg0 end +define dump_compute_stack + dump-compute-stack $arg0 +end + # Python interface define dump_alu_function dump-alu-function $arg0 @@ -279,6 +283,7 @@ echo dump_token - Dump Token\n echo dump_alu_value - Dump ALUValue\n echo dump_alu_counters - Dump ALUCounters\n echo dump_alu_vec - Dump AluVec\n +echo dump_compute_stack - Dump compute stack (std::stack)\n echo dump_alu_function - Dump AluFunction\n echo dump_external_func_rec - Dump ExternalFunctionRec\n echo dump_python_func_collection - Dump PythonFunctionCollection\n diff --git a/specs/src/gdb/specs_gdb.py b/specs/src/gdb/specs_gdb.py index 3a1071e..1a023c5 100644 --- a/specs/src/gdb/specs_gdb.py +++ b/specs/src/gdb/specs_gdb.py @@ -763,6 +763,145 @@ def invoke(self, arg, from_tty): # DUMP COMMANDS - Item Hierarchy # ============================================================================ +# Helper functions for dumping derived Item classes +def _dump_data_field_details(val): + """Dump DataField-specific fields.""" + print(f"DataField:") + label = chr(int(val["m_label"])) if int(val["m_label"]) > 0 else "none" + tail_label = chr(int(val["m_tailLabel"])) if int(val["m_tailLabel"]) > 0 else "none" + out_start = int(val["m_outStart"]) + max_len = int(val["m_maxLength"]) + strip = bool(val["m_strip"]) + conv = int(val["m_conversion"]) + align = int(val["m_alignment"]) + + conv_str = STRING_CONVERSIONS.get(conv, f"Unknown({conv})") + align_str = OUTPUT_ALIGNMENT.get(align, f"Unknown({align})") + + print(f" Label: {label}/{tail_label}") + print(f" Output Start: {out_start}") + print(f" Max Length: {max_len}") + print(f" Strip: {strip}") + print(f" Conversion: {conv_str}") + print(f" Alignment: {align_str}") + + # m_InputPart (shared_ptr) + try: + ip_ptr = val["m_InputPart"]["_M_ptr"] + if int(ip_ptr) == 0: + print(f" Input Part: ") + else: + result = gdb.parse_and_eval( + f'((InputPart*)({int(ip_ptr)}))->Debug()') + debug_str = std_string_to_str(result) + print(f" Input Part: {debug_str}") + except Exception as ex: + print(f" Input Part: ") + + # AluVec fields + _dump_alu_vec_field(val, "m_outputStartExpression", "Output Start Expression") + _dump_alu_vec_field(val, "m_outputWidthExpression", "Output Width Expression") + _dump_alu_vec_field(val, "m_outputAlignmentExpression", "Output Alignment Expression") + +def _dump_alu_vec_field(val, field_name, label): + """Print an AluVec field on one line, or 'empty' if it has no elements.""" + try: + vec = val[field_name] + size = std_vector_size(vec) + if size == 0: + print(f" {label}: empty") + else: + start = vec["_M_impl"]["_M_start"] + items = [] + for i in range(size): + try: + ptr = start[i]["_M_ptr"] + if int(ptr) != 0: + result = gdb.parse_and_eval( + f'((AluUnit*)({int(ptr)}))->_identify()') + items.append(std_string_to_str(result)) + else: + items.append("") + except: + items.append("?") + print(f" {label}: {'; '.join(items)}") + except Exception as ex: + print(f" {label}: ") + +def _dump_token_item_details(val): + """Dump TokenItem-specific fields.""" + print(f"TokenItem:") + token = deref_shared_ptr(val["mp_Token"]) + if token: + type_val = int(token["m_type"]) + type_str = TOKEN_TYPES.get(type_val, f"Unknown({type_val})") + print(f" Token type: {type_str}") + else: + print(f" mp_Token: ") + +def _dump_set_item_details(val): + """Dump SetItem-specific fields.""" + print(f"SetItem:") + raw_expr = std_string_to_str(val["m_rawExpression"]) + key = int(val["m_key"]) + print(f" Expression: \"{raw_expr}\"") + print(f" Key: {key}") + +def _dump_skip_item_details(val): + """Dump SkipItem-specific fields.""" + print(f"SkipItem:") + raw_expr = std_string_to_str(val["m_rawExpression"]) + is_until = bool(val["m_bIsUntil"]) + satisfied = bool(val["m_bSatisfied"]) + skip_type = "SKIPUNTIL" if is_until else "SKIPWHILE" + print(f" Type: {skip_type}") + print(f" Expression: \"{raw_expr}\"") + print(f" Satisfied: {satisfied}") + +def _dump_condition_item_details(val): + """Dump ConditionItem-specific fields.""" + print(f"ConditionItem:") + pred = int(val["m_pred"]) + pred_str = CONDITION_PREDICATE.get(pred, f"Unknown({pred})") + raw_expr = std_string_to_str(val["m_rawExpression"]) + is_assn = bool(val["m_isAssignment"]) + print(f" Predicate: {pred_str}") + print(f" Expression: \"{raw_expr}\"") + print(f" Is Assignment: {is_assn}") + +def _dump_break_item_details(val): + """Dump BreakItem-specific fields.""" + print(f"BreakItem:") + ident = chr(int(val["m_identifier"])) + print(f" Identifier: {ident}") + +def _dump_context_item_details(val): + """Dump ContextItem-specific fields.""" + print(f"ContextItem:") + offset = int(val["m_offset"]) + print(f" Offset: {offset}") + +def _dump_select_item_details(val): + """Dump SelectItem-specific fields.""" + print(f"SelectItem:") + stream = int(val["m_stream"]) + b_output = bool(val["bOutput"]) + print(f" Stream: {stream}") + print(f" Output: {b_output}") + +def _dump_split_item_details(val): + """Dump SplitItem-specific fields.""" + print(f"SplitItem:") + is_field = bool(val["m_isField"]) + sep = std_string_to_str(val["m_separator"]) + splitting = bool(val["m_splitting"]) + current_piece = int(val["m_currentPiece"]) + split_type = "SPLITF" if is_field else "SPLITW" + print(f" Type: {split_type}") + print(f" Separator: \"{sep}\"") + print(f" Splitting: {splitting}") + print(f" Current Piece: {current_piece}") + class DumpItem(gdb.Command): """Dump an Item (polymorphic).""" @@ -809,6 +948,47 @@ def invoke(self, arg, from_tty): pass print("----- end of 'Item' dump") + + # Determine the dynamic type and dump derived-class-specific info. + # `val` may be a pointer (e.g. Item*), or an object/reference + # (e.g. when called with `*this`). Resolve it to the concrete + # object cast to its dynamic type so derived fields are accessible. + type_name = "Item" + derived = None + try: + obj = val + if obj.type.strip_typedefs().code == gdb.TYPE_CODE_PTR: + obj = obj.dereference() + dyn_type = obj.dynamic_type + type_name = dyn_type.name or type_name + # Cast to the dynamic type so derived-class fields can be read + derived = obj.cast(dyn_type) + except Exception: + derived = None + + # Dispatch to appropriate helper based on dynamic type + if derived is not None: + try: + if "DataField" in type_name: + _dump_data_field_details(derived) + elif "TokenItem" in type_name: + _dump_token_item_details(derived) + elif "SetItem" in type_name: + _dump_set_item_details(derived) + elif "SkipItem" in type_name: + _dump_skip_item_details(derived) + elif "ConditionItem" in type_name: + _dump_condition_item_details(derived) + elif "BreakItem" in type_name: + _dump_break_item_details(derived) + elif "ContextItem" in type_name: + _dump_context_item_details(derived) + elif "SelectItem" in type_name: + _dump_select_item_details(derived) + elif "SplitItem" in type_name: + _dump_split_item_details(derived) + except Exception as e: + print(f"Error dumping derived-class details: {e}") except Exception as e: print(f"Error: {e}") @@ -825,72 +1005,8 @@ def invoke(self, arg, from_tty): if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"DataField:") - val = gdb.parse_and_eval(arg) - label = chr(int(val["m_label"])) if int(val["m_label"]) > 0 else "none" - tail_label = chr(int(val["m_tailLabel"])) if int(val["m_tailLabel"]) > 0 else "none" - out_start = int(val["m_outStart"]) - max_len = int(val["m_maxLength"]) - strip = bool(val["m_strip"]) - conv = int(val["m_conversion"]) - align = int(val["m_alignment"]) - - conv_str = STRING_CONVERSIONS.get(conv, f"Unknown({conv})") - align_str = OUTPUT_ALIGNMENT.get(align, f"Unknown({align})") - - print(f" Label: {label}/{tail_label}") - print(f" Output Start: {out_start}") - print(f" Max Length: {max_len}") - print(f" Strip: {strip}") - print(f" Conversion: {conv_str}") - print(f" Alignment: {align_str}") - - # m_InputPart (shared_ptr) - try: - ip_ptr = val["m_InputPart"]["_M_ptr"] - if int(ip_ptr) == 0: - print(f" Input Part: ") - else: - result = gdb.parse_and_eval( - f'((InputPart*)({int(ip_ptr)}))->Debug()') - debug_str = std_string_to_str(result) - print(f" Input Part: {debug_str}") - except Exception as ex: - print(f" Input Part: ") - - # AluVec fields - self._dump_alu_vec_field(val, "m_outputStartExpression", "Output Start Expression") - self._dump_alu_vec_field(val, "m_outputWidthExpression", "Output Width Expression") - self._dump_alu_vec_field(val, "m_outputAlignmentExpression", "Output Alignment Expression") except Exception as e: print(f"Error: {e}") - - def _dump_alu_vec_field(self, val, field_name, label): - """Print an AluVec field on one line, or 'empty' if it has no elements.""" - try: - vec = val[field_name] - size = std_vector_size(vec) - if size == 0: - print(f" {label}: empty") - else: - start = vec["_M_impl"]["_M_start"] - items = [] - for i in range(size): - try: - ptr = start[i]["_M_ptr"] - if int(ptr) != 0: - result = gdb.parse_and_eval( - f'((AluUnit*)({int(ptr)}))->_identify()') - items.append(std_string_to_str(result)) - else: - items.append("") - except: - items.append("?") - print(f" {label}: {'; '.join(items)}") - except Exception as ex: - print(f" {label}: ") class DumpTokenItem(gdb.Command): """Dump a TokenItem.""" @@ -901,21 +1017,10 @@ def __init__(self): def invoke(self, arg, from_tty): try: - # First, call DumpItem to print base class fields + # Call DumpItem which now handles derived-class details if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"TokenItem:") - val = gdb.parse_and_eval(arg) - token = deref_shared_ptr(val["mp_Token"]) - if token: - type_val = int(token["m_type"]) - type_str = TOKEN_TYPES.get(type_val, f"Unknown({type_val})") - print(f" Token type: {type_str}") - else: - print(f" mp_Token: ") except Exception as e: print(f"Error: {e}") @@ -928,18 +1033,10 @@ def __init__(self): def invoke(self, arg, from_tty): try: - # First, call DumpItem to print base class fields + # Call DumpItem which now handles derived-class details if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"SetItem:") - val = gdb.parse_and_eval(arg) - raw_expr = std_string_to_str(val["m_rawExpression"]) - key = int(val["m_key"]) - print(f" Expression: \"{raw_expr}\"") - print(f" Key: {key}") except Exception as e: print(f"Error: {e}") @@ -952,21 +1049,10 @@ def __init__(self): def invoke(self, arg, from_tty): try: - # First, call DumpItem to print base class fields + # Call DumpItem which now handles derived-class details if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"SkipItem:") - val = gdb.parse_and_eval(arg) - raw_expr = std_string_to_str(val["m_rawExpression"]) - is_until = bool(val["m_bIsUntil"]) - satisfied = bool(val["m_bSatisfied"]) - skip_type = "SKIPUNTIL" if is_until else "SKIPWHILE" - print(f" Type: {skip_type}") - print(f" Expression: \"{raw_expr}\"") - print(f" Satisfied: {satisfied}") except Exception as e: print(f"Error: {e}") @@ -979,21 +1065,10 @@ def __init__(self): def invoke(self, arg, from_tty): try: - # First, call DumpItem to print base class fields + # Call DumpItem which now handles derived-class details if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"ConditionItem:") - val = gdb.parse_and_eval(arg) - pred = int(val["m_pred"]) - pred_str = CONDITION_PREDICATE.get(pred, f"Unknown({pred})") - raw_expr = std_string_to_str(val["m_rawExpression"]) - is_assn = bool(val["m_isAssignment"]) - print(f" Predicate: {pred_str}") - print(f" Expression: \"{raw_expr}\"") - print(f" Is Assignment: {is_assn}") except Exception as e: print(f"Error: {e}") @@ -1006,16 +1081,10 @@ def __init__(self): def invoke(self, arg, from_tty): try: - # First, call DumpItem to print base class fields + # Call DumpItem which now handles derived-class details if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"BreakItem:") - val = gdb.parse_and_eval(arg) - ident = chr(int(val["m_identifier"])) - print(f" Identifier: {ident}") except Exception as e: print(f"Error: {e}") @@ -1028,16 +1097,10 @@ def __init__(self): def invoke(self, arg, from_tty): try: - # First, call DumpItem to print base class fields + # Call DumpItem which now handles derived-class details if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"ContextItem:") - val = gdb.parse_and_eval(arg) - offset = int(val["m_offset"]) - print(f" Offset: {offset}") except Exception as e: print(f"Error: {e}") @@ -1050,18 +1113,10 @@ def __init__(self): def invoke(self, arg, from_tty): try: - # First, call DumpItem to print base class fields + # Call DumpItem which now handles derived-class details if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"SelectItem:") - val = gdb.parse_and_eval(arg) - stream = int(val["m_stream"]) - b_output = bool(val["bOutput"]) - print(f" Stream: {stream}") - print(f" Output: {b_output}") except Exception as e: print(f"Error: {e}") @@ -1074,23 +1129,10 @@ def __init__(self): def invoke(self, arg, from_tty): try: - # First, call DumpItem to print base class fields + # Call DumpItem which now handles derived-class details if self.dump_item is None: self.dump_item = DumpItem() self.dump_item.invoke(arg, from_tty) - - # Then print derived class fields - print(f"SplitItem:") - val = gdb.parse_and_eval(arg) - is_field = bool(val["m_isField"]) - sep = std_string_to_str(val["m_separator"]) - splitting = bool(val["m_splitting"]) - current_piece = int(val["m_currentPiece"]) - split_type = "SPLITF" if is_field else "SPLITW" - print(f" Type: {split_type}") - print(f" Separator: \"{sep}\"") - print(f" Splitting: {splitting}") - print(f" Current Piece: {current_piece}") except Exception as e: print(f"Error: {e}") @@ -1720,6 +1762,48 @@ def invoke(self, arg, from_tty): except Exception as e: print(f"Error: {e}") +class DumpComputeStack(gdb.Command): + """Dump the compute stack (std::stack) with all its items.""" + + def __init__(self): + super(DumpComputeStack, self).__init__("dump-compute-stack", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + try: + val = gdb.parse_and_eval(arg) + + # Extract stack items using the std_stack_items utility + # Items are shared_ptr, so we need to dereference them + items = std_stack_items(val) + size = len(items) + + print(f"Compute Stack @ {val.address} with {size} items:") + + if size == 0: + print(" (empty)") + else: + for i, item in enumerate(items): + try: + # Dereference the shared_ptr + alu_value = deref_shared_ptr(item) + if alu_value is None: + print(f" [{i}] (nil)") + else: + type_val = int(alu_value["m_type"]) + type_str = ALU_COUNTER_TYPE.get(type_val, f"Unknown({type_val})") + val_str = std_string_to_str(alu_value["m_value"]) + exact = bool(alu_value["m_exact"]) + exact_str = " (exact)" if exact else " (inexact)" + + if type_val == 0: # counterType__None + print(f" [{i}] (nil)") + else: + print(f" [{i}] ({type_str}) {val_str}{exact_str}") + except Exception as item_e: + print(f" [{i}] (error: {item_e})") + except Exception as e: + print(f"Error: {e}") + # ============================================================================ # DUMP COMMANDS - Utilities # ============================================================================ @@ -2247,6 +2331,7 @@ def register_commands(): DumpAluVec() DumpAluValueStats() DumpFrequencyMap() + DumpComputeStack() # Python interface commands DumpAluFunction()