https://github.com/python/cpython/commit/13b1659d6e8cb39436bf6b772725f61f33e6ec47
commit: 13b1659d6e8cb39436bf6b772725f61f33e6ec47
branch: 3.15
author: Pablo Galindo Salgado <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-09-24T23:52:22Z
summary:

[3.15] gh-154090: Store profiling mode and capture settings in binary profiles 
(GH-154105) (#158144)

(cherry picked from commit d3663ef943d984f4c8e349d705b61f393385d817)

files:
A Misc/NEWS.d/next/Library/2026-07-19-14-00-00.gh-issue-154090.q2kH1b.rst
M Include/internal/pycore_global_objects_fini_generated.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_runtime_init_generated.h
M Include/internal/pycore_unicodeobject_generated.h
M InternalDocs/profiling_binary_format.md
M Lib/profiling/sampling/binary_collector.py
M Lib/profiling/sampling/binary_reader.py
M Lib/profiling/sampling/cli.py
M Lib/profiling/sampling/stack_collector.py
M Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py
M Lib/test/test_profiling/test_sampling_profiler/test_cli.py
M Lib/test/test_profiling/test_sampling_profiler/test_collectors.py
M Modules/_remote_debugging/binary_io.h
M Modules/_remote_debugging/binary_io_reader.c
M Modules/_remote_debugging/binary_io_writer.c
M Modules/_remote_debugging/clinic/module.c.h
M Modules/_remote_debugging/module.c

diff --git a/Include/internal/pycore_global_objects_fini_generated.h 
b/Include/internal/pycore_global_objects_fini_generated.h
index bb7524bfc7cff4..c536079498e9cb 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -1641,6 +1641,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(canonical));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capath));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capitals));
+    _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capture_features));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(category));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cb_type));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(certfile));
diff --git a/Include/internal/pycore_global_strings.h 
b/Include/internal/pycore_global_strings.h
index 7f5e4c423b60ea..daa1f2ad2f109d 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -364,6 +364,7 @@ struct _Py_global_strings {
         STRUCT_FOR_ID(canonical)
         STRUCT_FOR_ID(capath)
         STRUCT_FOR_ID(capitals)
+        STRUCT_FOR_ID(capture_features)
         STRUCT_FOR_ID(category)
         STRUCT_FOR_ID(cb_type)
         STRUCT_FOR_ID(certfile)
diff --git a/Include/internal/pycore_runtime_init_generated.h 
b/Include/internal/pycore_runtime_init_generated.h
index 05e22b21560575..5c6635e6c3c232 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -1639,6 +1639,7 @@ extern "C" {
     INIT_ID(canonical), \
     INIT_ID(capath), \
     INIT_ID(capitals), \
+    INIT_ID(capture_features), \
     INIT_ID(category), \
     INIT_ID(cb_type), \
     INIT_ID(certfile), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h 
b/Include/internal/pycore_unicodeobject_generated.h
index da8ced205f3438..ddda6b467ce1c1 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -1236,6 +1236,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) 
{
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
     assert(PyUnicode_GET_LENGTH(string) != 1);
+    string = &_Py_ID(capture_features);
+    _PyUnicode_InternStatic(interp, &string);
+    assert(_PyUnicode_CheckConsistency(string, 1));
+    assert(PyUnicode_GET_LENGTH(string) != 1);
     string = &_Py_ID(category);
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
diff --git a/InternalDocs/profiling_binary_format.md 
b/InternalDocs/profiling_binary_format.md
index 51b1944aa5c8a0..0947e946f41bd0 100644
--- a/InternalDocs/profiling_binary_format.md
+++ b/InternalDocs/profiling_binary_format.md
@@ -84,15 +84,23 @@ with a single seek to `file_size - 32`, without first 
reading the header.
 |        |      |         | reserved)                              |
 |   12   |  8   | uint64  | Start timestamp (microseconds)         |
 |   20   |  8   | uint64  | Sample interval (microseconds)         |
-|   28   |  4   | uint32  | Total sample count                     |
-|   32   |  4   | uint32  | Thread count                           |
-|   36   |  8   | uint64  | String table offset                    |
-|   44   |  8   | uint64  | Frame table offset                     |
-|   52   |  4   | uint32  | Compression type (0=none, 1=zstd)      |
-|   56   |  8   | bytes   | Reserved (zero-filled)                 |
+|   28   |  8   | uint64  | Total sample count                     |
+|   36   |  4   | uint32  | Thread count                           |
+|   40   |  8   | uint64  | String table offset                    |
+|   48   |  8   | uint64  | Frame table offset                     |
+|   56   |  4   | uint32  | Compression type (0=none, 1=zstd)      |
+|   60   |  4   | uint32  | Profiling configuration bit field      |
 +--------+------+---------+----------------------------------------+
 ```
 
+The low three configuration bits contain the
+`_remote_debugging.PROFILING_MODE_*` value plus one. Zero means that the mode
+was not recorded, which is also the value in binaries written before this
+field was defined. Bit 3 indicates that capture features are known; when set,
+bits 4 through 8 respectively record `--all-threads`, `--native`, GC frames,
+`--opcodes`, and `--blocking`. Remaining bits are reserved for future capture
+features.
+
 The magic number `0x54414348` ("TACH" for Tachyon) identifies the file format
 and also serves as an **endianness marker**. When read on a system with
 different byte order than the writer, it appears as `0x48434154`. The reader
@@ -567,9 +575,10 @@ one write() call (or feeds through the compression stream).
 ## Future Considerations
 
 The optional profile-statistics block provides an extensible metadata area.
-The 16-byte checksum field in the footer is currently unused. The version
-field allows incompatible changes with graceful rejection. New compression
-types could be added (compression_type > 1).
+The Python-version field retains one reserved byte. The 16-byte checksum
+field in the footer is currently unused. The version field allows
+incompatible changes with graceful rejection. New compression types could
+be added (compression_type > 1).
 
 Any changes that alter the meaning of existing fields or the parsing logic
 should increment the version number to prevent older readers from
diff --git a/Lib/profiling/sampling/binary_collector.py 
b/Lib/profiling/sampling/binary_collector.py
index ec6020078985ca..7a35044b1ee1d7 100644
--- a/Lib/profiling/sampling/binary_collector.py
+++ b/Lib/profiling/sampling/binary_collector.py
@@ -10,6 +10,23 @@
 COMPRESSION_NONE = 0
 COMPRESSION_ZSTD = 1
 
+CAPTURE_FEATURES = {
+    "all_threads": 1 << 0,
+    "native": 1 << 1,
+    "gc": 1 << 2,
+    "opcodes": 1 << 3,
+    "blocking": 1 << 4,
+}
+
+
+def encode_capture_config(capture_config):
+    if capture_config is None:
+        return -1
+    return sum(
+        bit for name, bit in CAPTURE_FEATURES.items()
+        if capture_config.get(name, False)
+    )
+
 
 def _resolve_compression(compression):
     """Resolve compression type from string or int.
@@ -49,7 +66,7 @@ class BinaryCollector(Collector):
     """
 
     def __init__(self, filename, sample_interval_usec, *, skip_idle=False,
-                 compression='auto'):
+                 compression='auto', mode=None, capture_config=None):
         """Create a new binary collector.
 
         Args:
@@ -57,6 +74,9 @@ def __init__(self, filename, sample_interval_usec, *, 
skip_idle=False,
             sample_interval_usec: Sampling interval in microseconds
             skip_idle: If True, skip idle threads (not used in binary format)
             compression: 'auto', 'zstd', 'none', or int (0=none, 1=zstd)
+            mode: Profiling mode, or None if unknown
+            capture_config: Mapping of capture feature names to booleans, or
+                None if the capture configuration is unknown
         """
         self.filename = filename
         self.sample_interval_usec = sample_interval_usec
@@ -65,7 +85,10 @@ def __init__(self, filename, sample_interval_usec, *, 
skip_idle=False,
         compression_type = _resolve_compression(compression)
         start_time_us = int(time.monotonic() * 1_000_000)
         self._writer = _remote_debugging.BinaryWriter(
-            filename, sample_interval_usec, start_time_us, 
compression=compression_type
+            filename, sample_interval_usec, start_time_us,
+            compression=compression_type,
+            mode=-1 if mode is None else mode,
+            capture_features=encode_capture_config(capture_config),
         )
 
     def collect(self, stack_frames, timestamp_us=None):
diff --git a/Lib/profiling/sampling/binary_reader.py 
b/Lib/profiling/sampling/binary_reader.py
index e611dfd45523f1..2087c0da7c43b6 100644
--- a/Lib/profiling/sampling/binary_reader.py
+++ b/Lib/profiling/sampling/binary_reader.py
@@ -6,6 +6,7 @@
 from .stack_collector import FlamegraphCollector, CollapsedStackCollector
 from .jsonl_collector import JsonlCollector
 from .pstats_collector import PstatsCollector
+from .binary_collector import CAPTURE_FEATURES
 
 
 class BinaryReader:
@@ -50,10 +51,21 @@ def get_info(self):
                 - string_count: Number of unique strings
                 - frame_count: Number of unique frames
                 - compression: Compression type used
+                - mode: Profiling mode, or None if not recorded
+                - capture_config: Capture feature mapping, or None if not
+                  recorded
         """
         if self._reader is None:
             raise RuntimeError("Reader not open. Use as context manager.")
-        return self._reader.get_info()
+        info = self._reader.get_info()
+        capture_features = info.pop("capture_features")
+        info["capture_config"] = (
+            None if capture_features is None else {
+                name: bool(capture_features & bit)
+                for name, bit in CAPTURE_FEATURES.items()
+            }
+        )
+        return info
 
     def replay_samples(self, collector, progress_callback=None):
         """Replay samples from binary file through a collector.
@@ -119,7 +131,7 @@ def convert_binary_to_format(input_file, output_file, 
output_format,
         elif output_format == 'gecko':
             collector = GeckoCollector(interval)
         elif output_format == "jsonl":
-            collector = JsonlCollector(interval)
+            collector = JsonlCollector(interval, mode=info.get("mode"))
         else:
             raise ValueError(f"Unknown output format: {output_format}")
 
@@ -127,6 +139,8 @@ def convert_binary_to_format(input_file, output_file, 
output_format,
         count = reader.replay_samples(collector, progress_callback)
         if hasattr(collector, "set_replay_stats"):
             collector.set_replay_stats(info)
+        if hasattr(collector, "set_mode"):
+            collector.set_mode(info.get("mode"))
 
         # Export to target format
         collector.export(output_file)
diff --git a/Lib/profiling/sampling/cli.py b/Lib/profiling/sampling/cli.py
index db50aefbf7bf52..67adf4c935038b 100644
--- a/Lib/profiling/sampling/cli.py
+++ b/Lib/profiling/sampling/cli.py
@@ -629,9 +629,20 @@ def _sort_to_mode(sort_choice):
     }
     return sort_map.get(sort_choice, SORT_MODE_NSAMPLES)
 
+
+def _capture_config_from_args(args):
+    return {
+        "all_threads": args.all_threads,
+        "native": args.native,
+        "gc": args.gc,
+        "opcodes": args.opcodes,
+        "blocking": args.blocking,
+    }
+
+
 def _create_collector(format_type, sample_interval_usec, skip_idle, 
opcodes=False,
                       mode=None, output_file=None, compression='auto',
-                      diff_baseline=None):
+                      diff_baseline=None, capture_config=None):
     """Create the appropriate collector based on format type.
 
     Args:
@@ -645,6 +656,7 @@ def _create_collector(format_type, sample_interval_usec, 
skip_idle, opcodes=Fals
         output_file: Output file path (required for binary format)
         compression: Compression type for binary format ('auto', 'zstd', 
'none')
         diff_baseline: Path to baseline binary file for differential flamegraph
+        capture_config: Capture feature mapping for binary profiles and diffs
 
     Returns:
         A collector instance of the appropriate type
@@ -661,7 +673,9 @@ def _create_collector(format_type, sample_interval_usec, 
skip_idle, opcodes=Fals
         return collector_class(
             sample_interval_usec,
             baseline_binary_path=diff_baseline,
-            skip_idle=skip_idle
+            skip_idle=skip_idle,
+            mode=mode,
+            capture_config=capture_config,
         )
 
     # Binary format requires output file and compression
@@ -669,7 +683,8 @@ def _create_collector(format_type, sample_interval_usec, 
skip_idle, opcodes=Fals
         if output_file is None:
             raise ValueError("Binary format requires an output file")
         return collector_class(output_file, sample_interval_usec, 
skip_idle=skip_idle,
-                              compression=compression)
+                              compression=compression, mode=mode,
+                              capture_config=capture_config)
 
     # Gecko format never skips idle (it needs both GIL and CPU data)
     # and is the only format that uses opcodes for interval markers
@@ -760,7 +775,9 @@ def _replay_with_reader(args, reader):
 
     collector = _create_collector(
         args.format, interval, skip_idle=False,
-        diff_baseline=args.diff_baseline
+        mode=info.get("mode"),
+        diff_baseline=args.diff_baseline,
+        capture_config=info.get("capture_config"),
     )
 
     def progress_callback(current, total):
@@ -778,6 +795,8 @@ def progress_callback(current, total):
     count = reader.replay_samples(collector, progress_callback)
     if hasattr(collector, "set_replay_stats"):
         collector.set_replay_stats(info)
+    if hasattr(collector, "set_mode"):
+        collector.set_mode(info.get("mode"))
     print()
 
     if args.format == "pstats":
@@ -791,7 +810,8 @@ def progress_callback(current, total):
             sort_mode = _sort_to_mode(sort_choice)
             collector.print_stats(
                 sort_mode, limit, not args.no_summary,
-                PROFILING_MODE_WALL
+                info.get("mode") if info.get("mode") is not None
+                else PROFILING_MODE_WALL
             )
     else:
         filename = (
@@ -1179,7 +1199,8 @@ def _handle_attach(args):
         args.format, args.sample_interval_usec, skip_idle, args.opcodes, mode,
         output_file=output_file,
         compression=getattr(args, 'compression', 'auto'),
-        diff_baseline=args.diff_baseline
+        diff_baseline=args.diff_baseline,
+        capture_config=_capture_config_from_args(args),
     )
 
     with _get_child_monitor_context(args, args.pid):
@@ -1286,7 +1307,8 @@ def _handle_run(args):
         args.format, args.sample_interval_usec, skip_idle, args.opcodes, mode,
         output_file=output_file,
         compression=getattr(args, 'compression', 'auto'),
-        diff_baseline=args.diff_baseline
+        diff_baseline=args.diff_baseline,
+        capture_config=_capture_config_from_args(args),
     )
 
     with _get_child_monitor_context(args, process.pid):
diff --git a/Lib/profiling/sampling/stack_collector.py 
b/Lib/profiling/sampling/stack_collector.py
index 8de460856666d7..1610f4a3655882 100644
--- a/Lib/profiling/sampling/stack_collector.py
+++ b/Lib/profiling/sampling/stack_collector.py
@@ -161,6 +161,8 @@ def set_replay_stats(self, info):
             missed_samples=info.get("missed_samples"),
             mode=self.stats.get("mode"),
         )
+    def set_mode(self, mode):
+        self.stats["mode"] = mode
 
     def export(self, filename):
         flamegraph_data = self._convert_to_flamegraph_format()
@@ -565,13 +567,16 @@ def _create_flamegraph_html(self, data):
 class DiffFlamegraphCollector(FlamegraphCollector):
     """Differential flamegraph collector that compares against a baseline 
binary profile."""
 
-    def __init__(self, sample_interval_usec, *, baseline_binary_path, 
skip_idle=False):
+    def __init__(self, sample_interval_usec, *, baseline_binary_path,
+                 skip_idle=False, mode=None, capture_config=None):
         super().__init__(sample_interval_usec, skip_idle=skip_idle)
         if not os.path.exists(baseline_binary_path):
             raise ValueError(f"Baseline file not found: 
{baseline_binary_path}")
         self.baseline_binary_path = baseline_binary_path
         self._baseline_collector = None
         self._elided_paths = set()
+        self.mode = mode
+        self.capture_config = capture_config
 
     def _load_baseline(self):
         """Load baseline profile from binary file."""
@@ -580,6 +585,32 @@ def _load_baseline(self):
         with BinaryReader(self.baseline_binary_path) as reader:
             info = reader.get_info()
 
+            baseline_mode = info.get("mode")
+            if (
+                baseline_mode is not None
+                and self.mode is not None
+                and baseline_mode != self.mode
+            ):
+                raise ValueError(
+                    "Baseline profiling mode does not match current mode"
+                )
+
+            baseline_config = info.get("capture_config")
+            if baseline_config is not None and self.capture_config is not None:
+                names = baseline_config.keys() | self.capture_config.keys()
+                mismatches = [
+                    name for name in names
+                    if baseline_config.get(name, False)
+                    != self.capture_config.get(name, False)
+                ]
+            else:
+                mismatches = []
+            if mismatches:
+                raise ValueError(
+                    "Baseline capture configuration does not match current "
+                    f"configuration: {', '.join(sorted(mismatches))}"
+                )
+
             baseline_collector = FlamegraphCollector(
                 sample_interval_usec=info['sample_interval_us'],
                 skip_idle=self.skip_idle
diff --git 
a/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py 
b/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py
index 253f419dae3c2e..ff944b3163ec3e 100644
--- a/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py
+++ b/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py
@@ -25,6 +25,7 @@
     )
     from profiling.sampling.binary_collector import BinaryCollector
     from profiling.sampling.binary_reader import BinaryReader, 
convert_binary_to_format
+    from profiling.sampling.constants import PROFILING_MODE_CPU
     from profiling.sampling.gecko_collector import GeckoCollector
 
     ZSTD_AVAILABLE = _remote_debugging.zstd_available()
@@ -151,19 +152,24 @@ def tearDown(self):
             if os.path.exists(f):
                 os.unlink(f)
 
-    def create_binary_file(self, samples, interval=1000, compression="none"):
+    def create_binary_file(self, samples, interval=1000, compression="none",
+                           mode=None, capture_config=None):
         """Create a test binary file and track it for cleanup."""
-        filename, _ = self.write_binary_file(samples, interval, compression)
+        filename, _ = self.write_binary_file(
+            samples, interval, compression, mode, capture_config
+        )
         return filename
 
-    def write_binary_file(self, samples, interval=1000, compression="none"):
+    def write_binary_file(self, samples, interval=1000, compression="none",
+                          mode=None, capture_config=None):
         """Like create_binary_file but also returns the writer collector."""
         with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f:
             filename = f.name
         self.temp_files.append(filename)
 
         collector = BinaryCollector(
-            filename, interval, compression=compression
+            filename, interval, compression=compression, mode=mode,
+            capture_config=capture_config,
         )
         for sample in samples:
             collector.collect(sample)
@@ -516,6 +522,37 @@ def test_sample_interval_preserved(self):
                     info = reader.get_info()
                     self.assertEqual(info["sample_interval_us"], interval)
 
+    def test_profiling_mode_preserved(self):
+        filename = self.create_binary_file([], mode=PROFILING_MODE_CPU)
+        with BinaryReader(filename) as reader:
+            self.assertEqual(reader.get_info()["mode"], PROFILING_MODE_CPU)
+
+    def test_missing_profiling_mode_is_unknown(self):
+        filename = self.create_binary_file([])
+        with BinaryReader(filename) as reader:
+            self.assertIsNone(reader.get_info()["mode"])
+
+    def test_capture_config_preserved(self):
+        capture_config = {
+            "all_threads": True,
+            "native": True,
+            "gc": False,
+            "opcodes": True,
+            "blocking": False,
+        }
+        filename = self.create_binary_file(
+            [], capture_config=capture_config
+        )
+        with BinaryReader(filename) as reader:
+            self.assertEqual(
+                reader.get_info()["capture_config"], capture_config
+            )
+
+    def test_missing_capture_config_is_unknown(self):
+        filename = self.create_binary_file([])
+        with BinaryReader(filename) as reader:
+            self.assertIsNone(reader.get_info()["capture_config"])
+
     def test_threads_interleaved_samples(self):
         """Multiple threads with interleaved varying samples."""
         samples = []
@@ -1673,8 +1710,10 @@ def test_original_stats_extension_remains_readable(self):
 class TestBinaryReplayToJsonl(BinaryFormatTestBase):
     """Tests for binary -> JSONL replay via convert_binary_to_format."""
 
-    def _replay_to_jsonl(self, samples, interval=1000):
-        bin_path = self.create_binary_file(samples, interval=interval)
+    def _replay_to_jsonl(self, samples, interval=1000, mode=None):
+        bin_path = self.create_binary_file(
+            samples, interval=interval, mode=mode
+        )
         with tempfile.NamedTemporaryFile(suffix=".jsonl", delete=False) as f:
             jsonl_path = f.name
         self.temp_files.append(jsonl_path)
@@ -1704,6 +1743,16 @@ def test_binary_replay_to_jsonl_basic(self):
         self.assertEqual(len(frame_defs), 1)
         self.assertEqual(frame_defs[0]["line"], 99)
 
+    def test_binary_replay_to_jsonl_preserves_mode(self):
+        frame = make_frame("hot.py", 99, "hot_func")
+        records = self._replay_to_jsonl(
+            [[make_interpreter(0, [make_thread(1, [frame])])]],
+            mode=PROFILING_MODE_CPU,
+        )
+
+        meta = next(record for record in records if record["type"] == "meta")
+        self.assertEqual(meta["mode"], "cpu")
+
     def test_binary_replay_to_jsonl_rle_weight_propagation(self):
         """RLE-batched identical samples land as a single agg entry with the 
right total."""
         frame = make_frame("rle.py", 42, "rle_func")
diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_cli.py 
b/Lib/test/test_profiling/test_sampling_profiler/test_cli.py
index 3448258eca5d6c..e5dcd72c27ef5c 100644
--- a/Lib/test/test_profiling/test_sampling_profiler/test_cli.py
+++ b/Lib/test/test_profiling/test_sampling_profiler/test_cli.py
@@ -23,11 +23,13 @@
     requires_remote_subprocess_debugging,
 )
 
+from profiling.sampling.binary_reader import BinaryReader
 from profiling.sampling.cli import (
     FORMAT_EXTENSIONS,
     _create_collector,
     _generate_output_filename,
     _handle_output,
+    _replay_with_reader,
     main,
 )
 from profiling.sampling.constants import (
@@ -963,6 +965,41 @@ def test_cli_replay_reader_errors_exit_cleanly(self):
             "Error: Unsupported format version 2",
         )
 
+    def test_cli_replay_propagates_recorded_mode(self):
+        reader = mock.MagicMock()
+        reader.get_info.return_value = {
+            "sample_interval_us": 1000,
+            "sample_count": 0,
+            "compression_type": 0,
+            "mode": PROFILING_MODE_CPU,
+            "capture_config": {"all_threads": True},
+        }
+        reader.replay_samples.return_value = 0
+        collector = mock.MagicMock()
+        collector.export.return_value = True
+        args = SimpleNamespace(
+            format="diff_flamegraph",
+            input_file="current.bin",
+            diff_baseline="baseline.bin",
+            outfile="diff.html",
+            browser=False,
+        )
+
+        with mock.patch(
+            "profiling.sampling.cli._create_collector",
+            return_value=collector,
+        ) as create_collector:
+            _replay_with_reader(args, reader)
+
+        create_collector.assert_called_once_with(
+            "diff_flamegraph",
+            1000,
+            skip_idle=False,
+            mode=PROFILING_MODE_CPU,
+            diff_baseline="baseline.bin",
+            capture_config={"all_threads": True},
+        )
+
     def test_cli_jsonl_format_mutually_exclusive_with_pstats(self):
         """--jsonl and --pstats cannot be combined (mutually exclusive 
group)."""
         with (
@@ -1005,6 +1042,26 @@ def 
test_cli_jsonl_create_collector_propagates_mode(self):
         meta = next(r for r in records if r["type"] == "meta")
         self.assertEqual(meta["mode"], "cpu")
 
+    def test_cli_binary_create_collector_propagates_mode(self):
+        with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f:
+            binary_path = f.name
+        self.addCleanup(os.unlink, binary_path)
+        collector = _create_collector(
+            "binary",
+            sample_interval_usec=1000,
+            skip_idle=True,
+            mode=PROFILING_MODE_CPU,
+            output_file=binary_path,
+            compression="none",
+            capture_config={"native": True},
+        )
+        collector.export(None)
+
+        with BinaryReader(binary_path) as reader:
+            info = reader.get_info()
+            self.assertEqual(info["mode"], PROFILING_MODE_CPU)
+            self.assertTrue(info["capture_config"]["native"])
+
     def test_cli_jsonl_rejects_opcodes_combination(self):
         """--opcodes is incompatible with --jsonl per 
opcodes_compatible_formats."""
         test_args = [
diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py 
b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py
index 069a72eb1c88a6..533ca36376e569 100644
--- a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py
+++ b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py
@@ -1638,6 +1638,50 @@ def test_diff_flamegraph_changed_functions(self):
         self.assertAlmostEqual(cold_node["diff"], -1.0)
         self.assertAlmostEqual(cold_node["diff_pct"], -50.0)
 
+    def test_diff_flamegraph_rejects_mismatched_profiling_modes(self):
+        from profiling.sampling.binary_collector import BinaryCollector
+        from profiling.sampling.stack_collector import DiffFlamegraphCollector
+
+        bin_file = tempfile.NamedTemporaryFile(suffix=".bin", delete=False)
+        self.addCleanup(close_and_unlink, bin_file)
+        writer = BinaryCollector(
+            bin_file.name,
+            sample_interval_usec=1000,
+            compression="none",
+            mode=PROFILING_MODE_CPU,
+        )
+        writer.export(None)
+
+        diff = DiffFlamegraphCollector(
+            1000,
+            baseline_binary_path=bin_file.name,
+            mode=PROFILING_MODE_WALL,
+        )
+        with self.assertRaisesRegex(ValueError, "profiling mode"):
+            diff._convert_to_flamegraph_format()
+
+    def test_diff_flamegraph_rejects_mismatched_capture_config(self):
+        from profiling.sampling.binary_collector import BinaryCollector
+        from profiling.sampling.stack_collector import DiffFlamegraphCollector
+
+        bin_file = tempfile.NamedTemporaryFile(suffix=".bin", delete=False)
+        self.addCleanup(close_and_unlink, bin_file)
+        writer = BinaryCollector(
+            bin_file.name,
+            sample_interval_usec=1000,
+            compression="none",
+            capture_config={"all_threads": True},
+        )
+        writer.export(None)
+
+        diff = DiffFlamegraphCollector(
+            1000,
+            baseline_binary_path=bin_file.name,
+            capture_config={"all_threads": False},
+        )
+        with self.assertRaisesRegex(ValueError, "all_threads"):
+            diff._convert_to_flamegraph_format()
+
     def test_diff_flamegraph_scale_factor(self):
         """Scale factor adjusts when sample counts differ."""
         baseline_frames = [
diff --git 
a/Misc/NEWS.d/next/Library/2026-07-19-14-00-00.gh-issue-154090.q2kH1b.rst 
b/Misc/NEWS.d/next/Library/2026-07-19-14-00-00.gh-issue-154090.q2kH1b.rst
new file mode 100644
index 00000000000000..f2d2b370241db0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-19-14-00-00.gh-issue-154090.q2kH1b.rst
@@ -0,0 +1,2 @@
+Record the profiling mode in Tachyon binary profiles and reject differential
+comparisons whose known modes do not match.
diff --git a/Modules/_remote_debugging/binary_io.h 
b/Modules/_remote_debugging/binary_io.h
index 615e3626e1cbc6..c936d3372e5acd 100644
--- a/Modules/_remote_debugging/binary_io.h
+++ b/Modules/_remote_debugging/binary_io.h
@@ -59,7 +59,9 @@ extern "C" {
 #define HDR_SIZE_FRAME_TABLE 8
 #define HDR_OFF_COMPRESSION  (HDR_OFF_FRAME_TABLE + HDR_SIZE_FRAME_TABLE)
 #define HDR_SIZE_COMPRESSION 4
-#define FILE_HEADER_SIZE     (HDR_OFF_COMPRESSION + HDR_SIZE_COMPRESSION)
+#define HDR_OFF_CONFIG       (HDR_OFF_COMPRESSION + HDR_SIZE_COMPRESSION)
+#define HDR_SIZE_CONFIG      4
+#define FILE_HEADER_SIZE     (HDR_OFF_CONFIG + HDR_SIZE_CONFIG)
 #define FILE_HEADER_PLACEHOLDER_SIZE 64
 
 static_assert(FILE_HEADER_SIZE <= FILE_HEADER_PLACEHOLDER_SIZE,
@@ -135,6 +137,19 @@ static_assert(PROFILE_STATS_SIZE == 56,
 #define COMPRESSION_NONE        0
 #define COMPRESSION_ZSTD        1
 
+/* Profiling configuration. The mode occupies the low three bits and is
+ * stored plus one so zero remains compatible with files written before this
+ * field was defined. Capture features are valid when bit 3 is set. */
+#define PROFILING_CONFIG_MODE_MASK       0x7U
+#define PROFILING_CONFIG_FEATURES_KNOWN  (1U << 3)
+#define PROFILING_CONFIG_FEATURES_SHIFT  4
+#define PROFILING_FEATURE_ALL_THREADS    (1U << 0)
+#define PROFILING_FEATURE_NATIVE         (1U << 1)
+#define PROFILING_FEATURE_GC             (1U << 2)
+#define PROFILING_FEATURE_OPCODES        (1U << 3)
+#define PROFILING_FEATURE_BLOCKING       (1U << 4)
+#define PROFILING_FEATURE_MASK           0x1FU
+
 /* Stack encoding types for delta compression */
 #define STACK_REPEAT            0x00  /* RLE: identical to previous, with 
count */
 #define STACK_FULL              0x01  /* Full stack (first sample or no match) 
*/
@@ -298,6 +313,8 @@ typedef struct {
     double missed_samples;
     uint32_t profile_stats_present;
     int has_profile_stats;
+    int profiling_mode;
+    int capture_features;
 
     /* String hash table: PyObject* -> uint32_t index */
     _Py_hashtable_t *string_hash;
@@ -373,6 +390,8 @@ typedef struct {
     int has_profile_stats;
     uint64_t string_table_offset;
     uint64_t frame_table_offset;
+    int profiling_mode;
+    int capture_features;
 
     /* Parsed string table: array of Python string objects */
     PyObject **strings;
@@ -559,6 +578,8 @@ grow_array_inplace(void **ptr_addr, size_t count, size_t 
*capacity, size_t elem_
  *   sample_interval_us: Sampling interval in microseconds
  *   compression_type: COMPRESSION_NONE or COMPRESSION_ZSTD
  *   start_time_us: Start timestamp in microseconds (from time.monotonic() * 
1e6)
+ *   profiling_mode: PROFILING_MODE_* value, or -1 if unknown
+ *   capture_features: PROFILING_FEATURE_* bit mask, or -1 if unknown
  *
  * Returns:
  *   New BinaryWriter* on success, NULL on failure (PyErr set)
@@ -567,7 +588,9 @@ BinaryWriter *binary_writer_create(
     PyObject *path,
     uint64_t sample_interval_us,
     int compression_type,
-    uint64_t start_time_us
+    uint64_t start_time_us,
+    int profiling_mode,
+    int capture_features
 );
 
 /*
diff --git a/Modules/_remote_debugging/binary_io_reader.c 
b/Modules/_remote_debugging/binary_io_reader.c
index 4e5ccaaabc7270..9625ee6f301f05 100644
--- a/Modules/_remote_debugging/binary_io_reader.c
+++ b/Modules/_remote_debugging/binary_io_reader.c
@@ -85,7 +85,7 @@ reader_parse_header(BinaryReader *reader, const uint8_t 
*data, size_t file_size)
     /* Read header fields with byte-swapping if needed */
     uint64_t start_time_us, sample_interval_us, string_table_offset, 
frame_table_offset;
     uint64_t sample_count;
-    uint32_t thread_count, compression_type;
+    uint32_t thread_count, compression_type, profiling_config;
 
     memcpy(&start_time_us, &data[HDR_OFF_START_TIME], HDR_SIZE_START_TIME);
     memcpy(&sample_interval_us, &data[HDR_OFF_INTERVAL], HDR_SIZE_INTERVAL);
@@ -94,6 +94,7 @@ reader_parse_header(BinaryReader *reader, const uint8_t 
*data, size_t file_size)
     memcpy(&string_table_offset, &data[HDR_OFF_STR_TABLE], HDR_SIZE_STR_TABLE);
     memcpy(&frame_table_offset, &data[HDR_OFF_FRAME_TABLE], 
HDR_SIZE_FRAME_TABLE);
     memcpy(&compression_type, &data[HDR_OFF_COMPRESSION], 
HDR_SIZE_COMPRESSION);
+    memcpy(&profiling_config, &data[HDR_OFF_CONFIG], HDR_SIZE_CONFIG);
 
     reader->start_time_us = SWAP64_IF(reader->needs_swap, start_time_us);
     reader->sample_interval_us = SWAP64_IF(reader->needs_swap, 
sample_interval_us);
@@ -102,6 +103,20 @@ reader_parse_header(BinaryReader *reader, const uint8_t 
*data, size_t file_size)
     reader->string_table_offset = SWAP64_IF(reader->needs_swap, 
string_table_offset);
     reader->frame_table_offset = SWAP64_IF(reader->needs_swap, 
frame_table_offset);
     reader->compression_type = (int)SWAP32_IF(reader->needs_swap, 
compression_type);
+    profiling_config = SWAP32_IF(reader->needs_swap, profiling_config);
+    uint32_t profiling_mode =
+        profiling_config & PROFILING_CONFIG_MODE_MASK;
+    if (profiling_mode > PROFILING_MODE_EXCEPTION + 1) {
+        PyErr_Format(PyExc_ValueError,
+                     "Invalid profiling mode in header: %u", profiling_mode);
+        return -1;
+    }
+    reader->profiling_mode = (int)profiling_mode - 1;
+    reader->capture_features =
+        profiling_config & PROFILING_CONFIG_FEATURES_KNOWN
+        ? (int)((profiling_config >> PROFILING_CONFIG_FEATURES_SHIFT) &
+                PROFILING_FEATURE_MASK)
+        : -1;
 
     return 0;
 }
@@ -1388,8 +1403,31 @@ binary_reader_get_info(BinaryReader *reader)
         Py_DECREF(error_rate);
         return NULL;
     }
+    PyObject *profiling_mode = reader->profiling_mode < 0
+        ? Py_NewRef(Py_None)
+        : PyLong_FromLong(reader->profiling_mode);
+    if (profiling_mode == NULL) {
+        Py_DECREF(py_version);
+        Py_DECREF(duration);
+        Py_DECREF(sample_rate);
+        Py_DECREF(error_rate);
+        Py_DECREF(missed_samples);
+        return NULL;
+    }
+    PyObject *capture_features = reader->capture_features < 0
+        ? Py_NewRef(Py_None)
+        : PyLong_FromLong(reader->capture_features);
+    if (capture_features == NULL) {
+        Py_DECREF(py_version);
+        Py_DECREF(profiling_mode);
+        Py_DECREF(duration);
+        Py_DECREF(sample_rate);
+        Py_DECREF(error_rate);
+        Py_DECREF(missed_samples);
+        return NULL;
+    }
     return Py_BuildValue(
-        "{s:I, s:N, s:K, s:K, s:K, s:I, s:I, s:I, s:i, s:N, s:N, s:N, s:N}",
+        "{s:I, s:N, s:K, s:K, s:K, s:I, s:I, s:I, s:i, s:N, s:N, s:N, s:N, 
s:N, s:N}",
         "version", BINARY_FORMAT_VERSION,
         "python_version", py_version,
         "start_time_us", reader->start_time_us,
@@ -1402,7 +1440,9 @@ binary_reader_get_info(BinaryReader *reader)
         "duration_sec", duration,
         "sample_rate", sample_rate,
         "error_rate", error_rate,
-        "missed_samples", missed_samples
+        "missed_samples", missed_samples,
+        "mode", profiling_mode,
+        "capture_features", capture_features
     );
 }
 
diff --git a/Modules/_remote_debugging/binary_io_writer.c 
b/Modules/_remote_debugging/binary_io_writer.c
index 753d0b0cc966d0..6af81515e7131d 100644
--- a/Modules/_remote_debugging/binary_io_writer.c
+++ b/Modules/_remote_debugging/binary_io_writer.c
@@ -728,7 +728,8 @@ write_sample_with_encoding(BinaryWriter *writer, 
ThreadEntry *entry,
 
 BinaryWriter *
 binary_writer_create(PyObject *path, uint64_t sample_interval_us, int 
compression_type,
-                     uint64_t start_time_us)
+                     uint64_t start_time_us, int profiling_mode,
+                     int capture_features)
 {
     BinaryWriter *writer = PyMem_Calloc(1, sizeof(BinaryWriter));
     if (!writer) {
@@ -739,6 +740,8 @@ binary_writer_create(PyObject *path, uint64_t 
sample_interval_us, int compressio
     writer->start_time_us = start_time_us;
     writer->sample_interval_us = sample_interval_us;
     writer->compression_type = compression_type;
+    writer->profiling_mode = profiling_mode;
+    writer->capture_features = capture_features;
 
     writer->write_buffer = PyMem_Malloc(WRITE_BUFFER_SIZE);
     if (!writer->write_buffer) {
@@ -1202,6 +1205,12 @@ binary_writer_finalize(BinaryWriter *writer)
     uint64_t frame_table_offset_u64 = (uint64_t)frame_table_offset;
     uint32_t thread_count_u32 = (uint32_t)writer->thread_count;
     uint32_t compression_type_u32 = (uint32_t)writer->compression_type;
+    uint32_t profiling_config_u32 = (uint32_t)(writer->profiling_mode + 1);
+    if (writer->capture_features >= 0) {
+        profiling_config_u32 |= PROFILING_CONFIG_FEATURES_KNOWN;
+        profiling_config_u32 |= (uint32_t)writer->capture_features
+            << PROFILING_CONFIG_FEATURES_SHIFT;
+    }
 
     uint8_t header[FILE_HEADER_SIZE] = {0};
     uint32_t magic = BINARY_FORMAT_MAGIC;
@@ -1218,6 +1227,7 @@ binary_writer_finalize(BinaryWriter *writer)
     memcpy(header + HDR_OFF_STR_TABLE, &string_table_offset_u64, 
HDR_SIZE_STR_TABLE);
     memcpy(header + HDR_OFF_FRAME_TABLE, &frame_table_offset_u64, 
HDR_SIZE_FRAME_TABLE);
     memcpy(header + HDR_OFF_COMPRESSION, &compression_type_u32, 
HDR_SIZE_COMPRESSION);
+    memcpy(header + HDR_OFF_CONFIG, &profiling_config_u32, HDR_SIZE_CONFIG);
     if (fwrite_checked_allow_threads(header, FILE_HEADER_SIZE, writer->fp) < 
0) {
         return -1;
     }
diff --git a/Modules/_remote_debugging/clinic/module.c.h 
b/Modules/_remote_debugging/clinic/module.c.h
index 341ba000a37a75..30fd0bbea020e5 100644
--- a/Modules/_remote_debugging/clinic/module.c.h
+++ b/Modules/_remote_debugging/clinic/module.c.h
@@ -717,7 +717,7 @@ _remote_debugging_GCMonitor_get_gc_stats(PyObject *self, 
PyObject *const *args,
 
 PyDoc_STRVAR(_remote_debugging_BinaryWriter___init____doc__,
 "BinaryWriter(filename, sample_interval_us, start_time_us, *,\n"
-"             compression=0)\n"
+"             compression=0, mode=-1, capture_features=-1)\n"
 "--\n"
 "\n"
 "High-performance binary writer for profiling data.\n"
@@ -728,6 +728,9 @@ PyDoc_STRVAR(_remote_debugging_BinaryWriter___init____doc__,
 "    start_time_us: Start timestamp in microseconds (from\n"
 "        time.monotonic() * 1e6)\n"
 "    compression: 0=none, 1=zstd (default: 0)\n"
+"    mode: Profiling mode, or -1 if unknown (default: -1)\n"
+"    capture_features: Capture feature bit mask, or -1 if unknown\n"
+"        (default: -1)\n"
 "\n"
 "Use as a context manager or call finalize() when done.");
 
@@ -736,7 +739,8 @@ 
_remote_debugging_BinaryWriter___init___impl(BinaryWriterObject *self,
                                              PyObject *filename,
                                              unsigned long long 
sample_interval_us,
                                              unsigned long long start_time_us,
-                                             int compression);
+                                             int compression, int mode,
+                                             int capture_features);
 
 static int
 _remote_debugging_BinaryWriter___init__(PyObject *self, PyObject *args, 
PyObject *kwargs)
@@ -744,7 +748,7 @@ _remote_debugging_BinaryWriter___init__(PyObject *self, 
PyObject *args, PyObject
     int return_value = -1;
     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
 
-    #define NUM_KEYWORDS 4
+    #define NUM_KEYWORDS 6
     static struct {
         PyGC_Head _this_is_not_used;
         PyObject_VAR_HEAD
@@ -753,7 +757,7 @@ _remote_debugging_BinaryWriter___init__(PyObject *self, 
PyObject *args, PyObject
     } _kwtuple = {
         .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
         .ob_hash = -1,
-        .ob_item = { &_Py_ID(filename), &_Py_ID(sample_interval_us), 
&_Py_ID(start_time_us), &_Py_ID(compression), },
+        .ob_item = { &_Py_ID(filename), &_Py_ID(sample_interval_us), 
&_Py_ID(start_time_us), &_Py_ID(compression), &_Py_ID(mode), 
&_Py_ID(capture_features), },
     };
     #undef NUM_KEYWORDS
     #define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -762,14 +766,14 @@ _remote_debugging_BinaryWriter___init__(PyObject *self, 
PyObject *args, PyObject
     #  define KWTUPLE NULL
     #endif  // !Py_BUILD_CORE
 
-    static const char * const _keywords[] = {"filename", "sample_interval_us", 
"start_time_us", "compression", NULL};
+    static const char * const _keywords[] = {"filename", "sample_interval_us", 
"start_time_us", "compression", "mode", "capture_features", NULL};
     static _PyArg_Parser _parser = {
         .keywords = _keywords,
         .fname = "BinaryWriter",
         .kwtuple = KWTUPLE,
     };
     #undef KWTUPLE
-    PyObject *argsbuf[4];
+    PyObject *argsbuf[6];
     PyObject * const *fastargs;
     Py_ssize_t nargs = PyTuple_GET_SIZE(args);
     Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 3;
@@ -777,6 +781,8 @@ _remote_debugging_BinaryWriter___init__(PyObject *self, 
PyObject *args, PyObject
     unsigned long long sample_interval_us;
     unsigned long long start_time_us;
     int compression = 0;
+    int mode = -1;
+    int capture_features = -1;
 
     fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, 
kwargs, NULL, &_parser,
             /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
@@ -793,12 +799,30 @@ _remote_debugging_BinaryWriter___init__(PyObject *self, 
PyObject *args, PyObject
     if (!noptargs) {
         goto skip_optional_kwonly;
     }
-    compression = PyLong_AsInt(fastargs[3]);
-    if (compression == -1 && PyErr_Occurred()) {
+    if (fastargs[3]) {
+        compression = PyLong_AsInt(fastargs[3]);
+        if (compression == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        if (!--noptargs) {
+            goto skip_optional_kwonly;
+        }
+    }
+    if (fastargs[4]) {
+        mode = PyLong_AsInt(fastargs[4]);
+        if (mode == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        if (!--noptargs) {
+            goto skip_optional_kwonly;
+        }
+    }
+    capture_features = PyLong_AsInt(fastargs[5]);
+    if (capture_features == -1 && PyErr_Occurred()) {
         goto exit;
     }
 skip_optional_kwonly:
-    return_value = 
_remote_debugging_BinaryWriter___init___impl((BinaryWriterObject *)self, 
filename, sample_interval_us, start_time_us, compression);
+    return_value = 
_remote_debugging_BinaryWriter___init___impl((BinaryWriterObject *)self, 
filename, sample_interval_us, start_time_us, compression, mode, 
capture_features);
 
 exit:
     return return_value;
@@ -1685,4 +1709,4 @@ _remote_debugging_get_gc_stats(PyObject *module, PyObject 
*const *args, Py_ssize
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=287128fc776bf710 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=88bcb21da5a25526 input=a9049054013a1b77]*/
diff --git a/Modules/_remote_debugging/module.c 
b/Modules/_remote_debugging/module.c
index 6d23f89c961fd8..abe996b46c1a4b 100644
--- a/Modules/_remote_debugging/module.c
+++ b/Modules/_remote_debugging/module.c
@@ -1717,6 +1717,8 @@ _remote_debugging.BinaryWriter.__init__
     start_time_us: unsigned_long_long
     *
     compression: int = 0
+    mode: int = -1
+    capture_features: int = -1
 
 High-performance binary writer for profiling data.
 
@@ -1726,6 +1728,9 @@ High-performance binary writer for profiling data.
     start_time_us: Start timestamp in microseconds (from
         time.monotonic() * 1e6)
     compression: 0=none, 1=zstd (default: 0)
+    mode: Profiling mode, or -1 if unknown (default: -1)
+    capture_features: Capture feature bit mask, or -1 if unknown
+        (default: -1)
 
 Use as a context manager or call finalize() when done.
 [clinic start generated code]*/
@@ -1735,14 +1740,26 @@ 
_remote_debugging_BinaryWriter___init___impl(BinaryWriterObject *self,
                                              PyObject *filename,
                                              unsigned long long 
sample_interval_us,
                                              unsigned long long start_time_us,
-                                             int compression)
-/*[clinic end generated code: output=00446656ea2e5986 input=2e3f298c69fc7666]*/
+                                             int compression, int mode,
+                                             int capture_features)
+/*[clinic end generated code: output=3c1c9576795658ce input=98add735b20403ad]*/
 {
+    if (mode < -1 || mode > PROFILING_MODE_EXCEPTION) {
+        PyErr_SetString(PyExc_ValueError, "invalid profiling mode");
+        return -1;
+    }
+    if (capture_features < -1 ||
+        capture_features > (int)PROFILING_FEATURE_MASK) {
+        PyErr_SetString(PyExc_ValueError, "invalid capture features");
+        return -1;
+    }
     if (self->writer) {
         binary_writer_destroy(self->writer);
     }
 
-    self->writer = binary_writer_create(filename, sample_interval_us, 
compression, start_time_us);
+    self->writer = binary_writer_create(
+        filename, sample_interval_us, compression, start_time_us, mode,
+        capture_features);
     if (!self->writer) {
         return -1;
     }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to