kconfig: collect and save trace data

Collect and save trace data for all symbols in the merged configuration.
This includes information about where each symbol was defined or
assigned, which can be useful for debugging complex Kconfig setups.

The trace data includes the following information for each symbol:
- Name
- Visibility
- Type
- Value
- Kind and location of value origin (as defined in kconfiglib)

The trace data is saved for later use in two formats: a binary pickle
file and a human-readable JSON file.

Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
diff --git a/scripts/kconfig/kconfig.py b/scripts/kconfig/kconfig.py
index 4e4e086..ddc98e7 100755
--- a/scripts/kconfig/kconfig.py
+++ b/scripts/kconfig/kconfig.py
@@ -16,7 +16,9 @@
 # Also does various checks (most via Kconfiglib warnings).
 
 import argparse
+import json
 import os
+import pickle
 import re
 import sys
 import textwrap
@@ -29,7 +31,9 @@
     OR,
     TRI_TO_STR,
     TRISTATE,
+    TYPE_TO_STR,
     Kconfig,
+    Symbol,
     expr_str,
     expr_value,
     split_expr,
@@ -132,6 +136,13 @@
     print(kconf.write_config(args.config_out))
     print(kconf.write_autoconf(args.header_out))
 
+    # Write value origin information for the merged configuration
+    trace_data = collect_trace_data(kconf)
+    with open(args.config_out + '-trace.pickle', 'wb') as f:
+        pickle.dump(trace_data, f)
+    with open(args.config_out + '-trace.json', 'w') as f:
+        json.dump(trace_data, f, indent=2)
+
     # Write the list of parsed Kconfig files to a file
     write_kconfig_filenames(kconf, args.kconfig_list_out)
 
@@ -286,6 +297,44 @@
     return not any(node.prompt for node in sym.nodes)
 
 
+def collect_trace_data(kconf):
+    """
+    Collects trace data for all symbols in 'kconf'. The output is currently a
+    list of 6-tuples with one entry per symbol definition, with the following
+    layout:
+
+        (name, visibility, type, value, kind, location)
+
+    where the first 4 entries are the string representation of the symbol's
+    properties, and 'kind' and 'location' are taken from its 'origin'
+    attribute.
+    """
+
+    # NOTE: this data is used by scripts/kconfig/traceconfig.py and the tests
+    # under tests/kconfig/tracing. Make sure to keep them aligned if the
+    # format changes in any way.
+
+    trace_data = []
+    for node in kconf.node_iter(True):
+        item = node.item
+        if not isinstance(item, Symbol):
+            continue
+
+        origin = item.origin
+        if origin is None:
+            continue
+
+        name = kconf.config_prefix + item.name
+        kind, loc = origin
+        value = None if kind == "unset" else item.str_value
+
+        trace_entry = (name, TRI_TO_STR[item.visibility],
+                       TYPE_TO_STR[item.type], value, kind, loc)
+        trace_data.append(trace_entry)
+
+    return trace_data
+
+
 def write_kconfig_filenames(kconf, kconfig_list_path):
     # Writes a sorted list with the absolute paths of all parsed Kconfig files
     # to 'kconfig_list_path'. The paths are realpath()'d, and duplicates are