---
metadata:
  - name: generator
    content: Diplodoc Platform v5.50.6
alternate:
  - https://perforator.tech/docs/en/reference/language-support/python/merging.md
---
> **Documentation Index:** Fetch the complete configuration index at https://perforator.tech/docs/en/llms.txt

# Merging Python Stack with Native Stack

The native and Python stacks are collected separately for the same perf event. Afterwards these stacks are merged into a single stack for better visualization and analysis.

## Since CPython 3.12

### Stub Frames

When C code starts evaluating Python code through CPython API, it pushes a stub frame. Each `_PyInterpreterFrame` structure contains the `owner` field, which stores the `python_frame_owner` enum value.

```c
enum python_frame_owner : u8 {
    FRAME_OWNED_BY_THREAD = 0,
    FRAME_OWNED_BY_GENERATOR = 1,
    FRAME_OWNED_BY_FRAME_OBJECT = 2,
    FRAME_OWNED_BY_CSTACK = 3,
};
```

If the value is equal to `FRAME_OWNED_BY_CSTACK`, then the frame is a stub frame.

A stub frame is a delimiter between the native and Python stacks. This frame is pushed onto the native stack in the `_PyEval_EvalFrameDefault` function.

### Algorithm

The Python user stack is divided into segments each one starting with a stub frame. Also, segments of the native stack with CPython are extracted using `_PyEval_EvalFrameDefault` as a delimiter. The functions starting with the `_Py` or `Py` prefix are considered to be CPython internal implementation.

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1002.4140625 506.8828125" width="2004.828125" height="1013.765625"><!--svg-source:excalidraw--><metadata/><defs></defs><rect x="0" y="0" width="1002.4140625" height="506.8828125" fill="#ffffff"/><g stroke-linecap="round" transform="translate(10.32421875 51.6953125) rotate(0 114.013671875 222.59375)"><path d="M32 0 C90 0, 148 0, 196.03 0 M32 0 C92.17 0, 152.35 0, 196.03 0 M196.03 0 C217.36 0, 228.03 10.67, 228.03 32 M196.03 0 C217.36 0, 228.03 10.67, 228.03 32 M228.03 32 C228.03 176.16, 228.03 320.33, 228.03 413.19 M228.03 32 C228.03 123.86, 228.03 215.72, 228.03 413.19 M228.03 413.19 C228.03 434.52, 217.36 445.19, 196.03 445.19 M228.03 413.19 C228.03 434.52, 217.36 445.19, 196.03 445.19 M196.03 445.19 C156.36 445.19, 116.7 445.19, 32 445.19 M196.03 445.19 C145.11 445.19, 94.18 445.19, 32 445.19 M32 445.19 C10.67 445.19, 0 434.52, 0 413.19 M32 445.19 C10.67 445.19, 0 434.52, 0 413.19 M0 413.19 C0 335.12, 0 257.05, 0 32 M0 413.19 C0 322.06, 0 230.94, 0 32 M0 32 C0 10.67, 10.67 0, 32 0 M0 32 C0 10.67, 10.67 0, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"/></g><g transform="translate(88.3125 70.1484375) rotate(0 27.850997924804688 9.682942793281654)"><text x="0" y="13.649076161409784" font-family="Virgil, Segoe UI Emoji" font-size="15.492708469250607px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">_start</text></g><g transform="translate(48.78125 109.578125) rotate(0 72.19483702017459 10.25115087095719)"><text x="0" y="14.450022267701247" font-family="Virgil, Segoe UI Emoji" font-size="16.401841393531495px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">_libc_start_main</text></g><g transform="translate(39.29296875 152.37890625) rotate(0 83.7999430000782 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">PyObject_CallMethod</text></g><g transform="translate(12.5859375 195.921875) rotate(0 110.10392761230469 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">_PyEval_EvalFrameDefault</text></g><g transform="translate(56.81794738769531 330.89453125) rotate(0 67.58393932878971 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">zstd_decompress</text></g><g stroke-linecap="round"><g transform="translate(10 98.0703125) rotate(0 113.818359375 -0.662109375)"><path d="M0 0 C37.94 -0.22, 189.7 -1.1, 227.64 -1.32 M0 0 C37.94 -0.22, 189.7 -1.1, 227.64 -1.32" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(11.75 138.84375) rotate(0 112.982421875 -0.98046875)"><path d="M0 0 C37.66 -0.33, 188.3 -1.63, 225.96 -1.96 M0 0 C37.66 -0.33, 188.3 -1.63, 225.96 -1.96" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(10.30078125 179.91015625) rotate(0 114.490234375 -0.96484375)"><path d="M0 0 C38.16 -0.32, 190.82 -1.61, 228.98 -1.93 M0 0 C38.16 -0.32, 190.82 -1.61, 228.98 -1.93" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(10.6796875 229.71875) rotate(0 114.765625 -1.171875)"><path d="M0 0 C38.26 -0.39, 191.28 -1.95, 229.53 -2.34 M0 0 C38.26 -0.39, 191.28 -1.95, 229.53 -2.34" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(10.79296875 272.19921875) rotate(0 114.21875 -0.212890625)"><path d="M0 0 C38.07 -0.07, 190.36 -0.35, 228.44 -0.43 M0 0 C38.07 -0.07, 190.36 -0.35, 228.44 -0.43" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(12.12109375 313.60546875) rotate(0 113.791015625 -0.2734375)"><path d="M0 0 C37.93 -0.09, 189.65 -0.46, 227.58 -0.55 M0 0 C37.93 -0.09, 189.65 -0.46, 227.58 -0.55" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g transform="translate(59.34375 16.0859375) rotate(0 64.50995084643364 12.5)"><text x="0" y="17.619999999999997" font-family="Virgil, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Native stack</text></g><g transform="translate(316.66796875 208.6640625) rotate(0 9.11400032043457 17.5)"><text x="0" y="24.668" font-family="Virgil, Segoe UI Emoji" font-size="28px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">+</text></g><g transform="translate(271.32421875 178.3046875) rotate(0 51.95995634794235 12.5)"><text x="0" y="17.619999999999997" font-family="Virgil, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Merge with</text></g><g stroke-linecap="round" transform="translate(405.462890625 44.8671875) rotate(0 114.013671875 222.59375)"><path d="M32 0 C68.36 0, 104.73 0, 196.03 0 M32 0 C91.74 0, 151.48 0, 196.03 0 M196.03 0 C217.36 0, 228.03 10.67, 228.03 32 M196.03 0 C217.36 0, 228.03 10.67, 228.03 32 M228.03 32 C228.03 165.02, 228.03 298.05, 228.03 413.19 M228.03 32 C228.03 180.4, 228.03 328.81, 228.03 413.19 M228.03 413.19 C228.03 434.52, 217.36 445.19, 196.03 445.19 M228.03 413.19 C228.03 434.52, 217.36 445.19, 196.03 445.19 M196.03 445.19 C143.86 445.19, 91.69 445.19, 32 445.19 M196.03 445.19 C137.59 445.19, 79.16 445.19, 32 445.19 M32 445.19 C10.67 445.19, 0 434.52, 0 413.19 M32 445.19 C10.67 445.19, 0 434.52, 0 413.19 M0 413.19 C0 269.8, 0 126.4, 0 32 M0 413.19 C0 266.42, 0 119.65, 0 32 M0 32 C0 10.67, 10.67 0, 32 0 M0 32 C0 10.67, 10.67 0, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"/></g><g transform="translate(451.5703125 10.89453125) rotate(0 64.7499478161335 12.5)"><text x="0" y="17.619999999999997" font-family="Virgil, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Python stack</text></g><g transform="translate(690.0930309295654 208.98828125) rotate(0 8.637992858886719 17.5)"><text x="0" y="24.668" font-family="Virgil, Segoe UI Emoji" font-size="28px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">=</text></g><g transform="translate(825.68359375 10) rotate(0 49.31996154785156 12.5)"><text x="0" y="17.619999999999997" font-family="Virgil, Segoe UI Emoji" font-size="20px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">Full stack</text></g><g transform="translate(418.796875 62.07421875) rotate(0 102.0965303812402 10.01783759823229)"><text x="0" y="14.121143878468207" font-family="Virgil, Segoe UI Emoji" font-size="16.02854015717163px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">&lt;trampoline python frame&gt;</text></g><g transform="translate(501.9453125 105.1328125) rotate(0 17.78163112350157 11.052734375)"><text x="0" y="15.579934375000002" font-family="Virgil, Segoe UI Emoji" font-size="17.684375000000003px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">main</text></g><g transform="translate(506.00390625 148.734375) rotate(0 13.007992014288902 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">foo</text></g><g stroke-linecap="round"><g transform="translate(406.3359375 96.0390625) rotate(0 113.857421875 0.24609375)"><path d="M0 0 C37.95 0.08, 189.76 0.41, 227.71 0.49 M0 0 C37.95 0.08, 189.76 0.41, 227.71 0.49" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(406.76953125 135.921875) rotate(0 113.2734375 0.01171875)"><path d="M0 0 C37.76 0, 188.79 0.02, 226.55 0.02 M0 0 C37.76 0, 188.79 0.02, 226.55 0.02" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(406.52734375 178.88671875) rotate(0 113.330078125 -0.51171875)"><path d="M0 0 C37.78 -0.17, 188.88 -0.85, 226.66 -1.02 M0 0 C37.78 -0.17, 188.88 -0.85, 226.66 -1.02" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g transform="translate(505.73828125 194.20703125) rotate(0 13.27199399471283 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">bar</text></g><g stroke-linecap="round"><g transform="translate(406.12109375 225.3515625) rotate(0 113.765625 0.26171875)"><path d="M0 0 C37.92 0.09, 189.61 0.44, 227.53 0.52 M0 0 C37.92 0.09, 189.61 0.44, 227.53 0.52" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g transform="translate(55.28515625 377.63671875) rotate(0 68.39994812011719 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">do_decompression</text></g><g stroke-linecap="round"><g transform="translate(11.6484375 365.25) rotate(0 113.3046875 -0.650390625)"><path d="M0 0 C37.77 -0.22, 188.84 -1.08, 226.61 -1.3 M0 0 C37.77 -0.22, 188.84 -1.08, 226.61 -1.3" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g transform="translate(45.61328125 241.5546875) rotate(0 83.19995215535164 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">PyEval_EvalFrameEx</text></g><g stroke-linecap="round"><g transform="translate(10.75390625 413.80859375) rotate(0 113.9609375 -0.314453125)"><path d="M0 0 C37.99 -0.1, 189.93 -0.52, 227.92 -0.63 M0 0 C37.99 -0.1, 189.93 -0.52, 227.92 -0.63" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g transform="translate(58.53515625 283.8203125) rotate(0 66.8239517211914 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">PyCFunction_Call</text></g><g stroke-linecap="round" transform="translate(762.52734375 45.75390625) rotate(0 114.013671875 222.59375)"><path d="M32 0 C83.26 0, 134.52 0, 196.03 0 M32 0 C84.78 0, 137.56 0, 196.03 0 M196.03 0 C217.36 0, 228.03 10.67, 228.03 32 M196.03 0 C217.36 0, 228.03 10.67, 228.03 32 M228.03 32 C228.03 136.48, 228.03 240.97, 228.03 413.19 M228.03 32 C228.03 158.73, 228.03 285.45, 228.03 413.19 M228.03 413.19 C228.03 434.52, 217.36 445.19, 196.03 445.19 M228.03 413.19 C228.03 434.52, 217.36 445.19, 196.03 445.19 M196.03 445.19 C157.06 445.19, 118.1 445.19, 32 445.19 M196.03 445.19 C157.53 445.19, 119.02 445.19, 32 445.19 M32 445.19 C10.67 445.19, 0 434.52, 0 413.19 M32 445.19 C10.67 445.19, 0 434.52, 0 413.19 M0 413.19 C0 328.43, 0 243.67, 0 32 M0 413.19 C0 323.09, 0 232.99, 0 32 M0 32 C0 10.67, 10.67 0, 32 0 M0 32 C0 10.67, 10.67 0, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"/></g><g transform="translate(840.515625 64.20703125) rotate(0 27.850997924804688 9.682942793281654)"><text x="0" y="13.649076161409784" font-family="Virgil, Segoe UI Emoji" font-size="15.492708469250607px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">_start</text></g><g transform="translate(800.984375 103.63671875) rotate(0 72.19483702017459 10.25115087095719)"><text x="0" y="14.450022267701247" font-family="Virgil, Segoe UI Emoji" font-size="16.401841393531495px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">_libc_start_main</text></g><g transform="translate(791.49609375 146.4375) rotate(0 83.7999430000782 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">PyObject_CallMethod</text></g><g transform="translate(777.25 189.98046875) rotate(0 101.93593713454902 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">&lt;trampoline python frame&gt;</text></g><g transform="translate(807.6773223876953 372.859375) rotate(0 67.58393932878971 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">zstd_decompress</text></g><g stroke-linecap="round"><g transform="translate(762.203125 92.12890625) rotate(0 113.818359375 -0.662109375)"><path d="M0 0 C37.94 -0.22, 189.7 -1.1, 227.64 -1.32 M0 0 C37.94 -0.22, 189.7 -1.1, 227.64 -1.32" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(763.953125 132.90234375) rotate(0 112.982421875 -0.98046875)"><path d="M0 0 C37.66 -0.33, 188.3 -1.63, 225.96 -1.96 M0 0 C37.66 -0.33, 188.3 -1.63, 225.96 -1.96" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(762.50390625 173.96875) rotate(0 114.490234375 -0.96484375)"><path d="M0 0 C38.16 -0.32, 190.82 -1.61, 228.98 -1.93 M0 0 C38.16 -0.32, 190.82 -1.61, 228.98 -1.93" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(762.8828125 223.77734375) rotate(0 114.765625 -1.171875)"><path d="M0 0 C38.26 -0.39, 191.28 -1.95, 229.53 -2.34 M0 0 C38.26 -0.39, 191.28 -1.95, 229.53 -2.34" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(762.99609375 266.2578125) rotate(0 114.21875 -0.212890625)"><path d="M0 0 C38.07 -0.07, 190.36 -0.35, 228.44 -0.43 M0 0 C38.07 -0.07, 190.36 -0.35, 228.44 -0.43" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(764.32421875 307.6640625) rotate(0 113.791015625 -0.2734375)"><path d="M0 0 C37.93 -0.09, 189.65 -0.46, 227.58 -0.55 M0 0 C37.93 -0.09, 189.65 -0.46, 227.58 -0.55" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g transform="translate(808.59765625 420.26953125) rotate(0 68.39994812011719 10)"><text x="0" y="14.096" font-family="Virgil, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">do_decompression</text></g><g stroke-linecap="round"><g transform="translate(763.8515625 359.30859375) rotate(0 113.3046875 -0.650390625)"><path d="M0 0 C37.77 -0.22, 188.84 -1.08, 226.61 -1.3 M0 0 C37.77 -0.22, 188.84 -1.08, 226.61 -1.3" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g transform="translate(851.51171875 230.63671875) rotate(0 20.873149067163467 12.925719269397916)"><text x="0" y="18.220093882143324" font-family="Virgil, Segoe UI Emoji" font-size="20.68115083103669px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">main</text></g><g stroke-linecap="round"><g transform="translate(762.95703125 407.8671875) rotate(0 113.9609375 -0.314453125)"><path d="M0 0 C37.99 -0.1, 189.93 -0.52, 227.92 -0.63 M0 0 C37.99 -0.1, 189.93 -0.52, 227.92 -0.63" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/><g transform="translate(859.609375 275.671875) rotate(0 14.771184681850684 11.35546875)"><text x="0" y="16.006668749999967" font-family="Virgil, Segoe UI Emoji" font-size="18.168749999999967px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">foo</text></g><g transform="translate(860.5078125 322.46875) rotate(0 15.649028856656514 11.791015625)"><text x="0" y="16.620615624999996" font-family="Virgil, Segoe UI Emoji" font-size="18.865624999999998px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="alphabetic">bar</text></g><g stroke-linecap="round"><g transform="translate(764.1640625 447.50390625) rotate(0 112.828125 0.220703125)"><path d="M0 0 C37.61 0.07, 188.05 0.37, 225.66 0.44 M0 0 C37.61 0.07, 188.05 0.37, 225.66 0.44" stroke="#1e1e1e" stroke-width="1" fill="none"/></g></g><mask/></svg>

These stack segments should map one-to-one with each other, but there are some exceptions:

* `_PyEval_EvalFrameDefault` has started executing on top of the native stack but has not finished pushing the stub Python frame yet.
* The native stack contains entries like `PyImport_ImportModule`. Python importlib may drop its own frames from the native stack.

The first case is handled easily, while the second case is more complex and is ignored for now.

## Before CPython 3.11

For CPython before 3.11, the process of stack merging is straightforward: map `PyEval_EvalFrameEx` frames (or `_PyEval_EvalFrameDefault` in later versions) from the native stack into collected Python frames one by one in their respective order.
