microsoft / microsoft/STL

`<stacktrace>`: investigate sporadic failure of `CaptureStackBackTrace`

Open
#3,889 14 comments 5 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
C++
Stars
11.1k
Forks
1.7k
Avg merge
4d 15h
Merged PRs (30d)
22

Description

When trying to solve #3859, I find that stacktrace::current can fail sporadically.

Repro (benchmark)
#include <benchmark/benchmark.h>
#include <iostream>
#include <stacktrace>

using namespace std;

#pragma optimize("", off)
stacktrace get_current(int extra_depth) {
    if (extra_depth > 0) {
        return get_current(extra_depth - 1);
    }
    else {
        return stacktrace::current(1);
    }
}
#pragma optimize("", on)

void BM_bef(benchmark::State& state) {
    const int extra_depth = state.range(0);
    for (auto _ : state) {
        stacktrace cur = get_current(extra_depth);
        if (cur.size() < extra_depth) {
            cout << extra_depth << "->" << cur.size() << endl;
            terminate();
        }
    }
}

BENCHMARK(BM_bef)->Arg(0)->Arg(100)->Arg(150)->Arg(300);

BENCHMARK_MAIN();
Potential result

image

image

After investigation, it turns out the failure is not due to memory exception in current. The function rely on CaptureStackBackTrace to catch frames, which just can fail sporadically:
https://github.com/microsoft/STL/blob/f51733ca5352c712165e46fdbc5d0d395971f0e7/stl/src/stacktrace.cpp#L252

Repro (CaptureStackBackTrace)
#include <Windows.h>
#include <iostream>

void* frames[1024];

#pragma optimize("", off)
long catch_some_frames(int extra_depth) {
    if (extra_depth > 0) {
        return catch_some_frames(extra_depth - 1);
    }
    else {
        return CaptureStackBackTrace(0, 1024, frames, nullptr);//sporadic crash: "300->0"
    }
}
#pragma optimize("", on)

int main() {
    for (;;) {
        auto caught = catch_some_frames(300);
        if (caught < 300) {
            std::cout << 300 << "->" << caught << std::endl;
            terminate();
        }
        else {
            std::cout << "*";
        }
    }
}
Potential result

image
image

CaptureStackBackTrace is a macro for RtlCaptureStackBackTrace. As to it randomly returning 0, I find these similar issues:
https://github.com/milostosic/MTuner/issues/66
https://developercommunity.visualstudio.com/t/capturestackbacktrace-randomly-fails-after-initial/1383213
And there might be other problems with it:
https://developercommunity.visualstudio.com/t/passing-framestoskip-greater-than-254-to-rtlcaptur/548661
Both the sporadic fail and skip-clamp behavior is not documented in its (documentation) (2).
And its API design is the root case for basic_stacktrace::current's efficiency problem.

We need to investigate what's going on with CaptureStackBackTrace when it returns 0. Also, we might be better off exploring other ways to capture stack frames, including implementing our own capture function in STL library. If possible, this will also give a chance to solve #3859 as a by-catch.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the CaptureStackBackTrace reproducer in the issue and inspect stl/src/stacktrace.cpp around line 252. Compare the sporadic zero-frame result and skip behavior against the linked Windows API documentation and reports. Done requires a documented root cause and an agreed direction for addressing the failure or replacing the capture approach.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
operating-systems, performance
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.