sentrydiscord.dev icon indicating copy to clipboard operation
sentrydiscord.dev copied to clipboard

C++ stack in the message embed being useless / contains no info

Open p0358 opened this issue 1 year ago • 8 comments

Hello, first of all -- thanks for this integration, it's pretty nice.

While using it with C++ I noticed though that the "Stack" section of the embed is pretty useless currently, I would appreciate considering looking into whether it could be improved: obraz

In comparison with Sentry's UI: obraz

All of the things seen in the above UI would are pretty important and would be good to be included:

  • library module name
  • relative offset value (to look up in debugger or disassembler)
  • function name, if available
  • file+line, if available

p0358 avatar Aug 30 '22 05:08 p0358

Hey! The Sentry payload is kind of adhoc - if you post an example one for your project I can definitely improve the parser

IanMitchell avatar Aug 30 '22 07:08 IanMitchell

The full payload is very long, I can send it to you somewhere privately because I'm not sure if it wouldn't reveal too much information in whole (just let me know where if you'd like, something like Twitter DM might be the simplest), but here's example last two stack frames:

            [...],
              {
                "function": "sub_18036DCA0",
                "symbol": "sub_18036DCA0",
                "package": "C:\\GAME\\Bin\\engine.dll",
                "in_app": false,
                "data": {
                  "orig_in_app": -1,
                  "symbolicator_status": "symbolicated"
                },
                "instruction_addr": "0x1d47b2bdcc5",
                "trust": "cfi"
              },
              {
                "function": "test_crash1",
                "raw_function": "test_crash1(CCommand const&)",
                "symbol": "test_crash1(CCommand const&)",
                "package": "C:\\GAME\\Bin\\tforevive.dll",
                "filename": "MiscConCommands.cpp",
                "abs_path": "D:\\a\\tforevive_cpp\\tforevive_cpp\\Hook\\MiscConCommands.cpp",
                "lineno": 99,
                "pre_context": [
                  "",
                  "void test_crash1(const CCommand& args)",
                  "{",
                  "    spdlog::warn(\"Testing crash 1\");"
                ],
                "context_line": "    std::cout << \"Oops:\" << *((int*)0);",
                "post_context": [
                  "}",
                  "",
                  "void test_crash2(const CCommand& args)",
                  "{",
                  "    spdlog::warn(\"Testing crash 2\");"
                ],
                "in_app": false,
                "data": {
                  "orig_in_app": -1,
                  "symbolicator_status": "symbolicated"
                },
                "instruction_addr": "0x7ffc1fbdea1d",
                "trust": "context"
              }

Out of this it's already possible to extract module name (package key, everything after last \, probably remove .dll extension (consider / and other extensions in case of non-Windows)).

Notice that for the first library the information is limited, as there's only a debug file available, with no source code. Similarly the symbols are sometimes missing, in such case there's only package and instruction_addr to work with (no function name)...

Now unfortunately instruction_addr is absolute, and due to ASLR wouldn't tell us anything, so in order to display a relative address, the parser will need to dive into the modules section, find the corresponding module, and subtract its base address. This is what Sentry's UI seems to be doing on the fly.

They seem to be located in a [...]->debug_meta->images array in the JSON tree (debug_meta is on the same object level as tags, right next to it).

It seems that location of code_file can be compared to find the right module, and then image_addr can be subtracted from instruction_addr in the stack.

    "debug_meta": {
      "images": [
        {
          "code_id": "4df2bcacd2000",
          "code_file": "C:\\WINDOWS\\SYSTEM32\\MSVCR100.dll",
          "debug_id": "25656ec1-1750-43b6-96d8-93aea9e1984b-1",
          "debug_file": "msvcr100.amd64.pdb",
          "image_addr": "0x56af0000",
          "image_size": 860160,
          "debug_status": "unused",
          "features": {
            "has_debug_info": false,
            "has_sources": false,
            "has_symbols": false,
            "has_unwind_info": false
          },
          "unwind_status": "unused",
          "type": "pe"
        },
        {
          "code_id": "59ae294e31d4000",
          "code_file": "C:\\GAME\\Bin\\engine.dll",
          "debug_id": "65417b2c-dd48-464f-90c1-a36aebe8c8da-1",
          "debug_file": "C:\\Game\\Live\\src\\engine\\Retail\\x64\\engine.pdb",
          "arch": "x86_64",
          "image_addr": "0x1d47af50000",
          "image_size": 52248576,
          "candidates": [
            {
              "download": {
                "status": "notfound"
              },
              "source": "sentry:amd",
              "source_name": "AMD"
            },
            {
              "download": {
                "details": "",
                "status": "noperm"
              },
              "source": "sentry:intel",
              "source_name": "Intel"
            },
            {
              "download": {
                "details": "",
                "status": "noperm"
              },
              "source": "sentry:intel",
              "source_name": "Intel"
            },
            {
              "download": {
                "details": "",
                "status": "noperm"
              },
              "source": "sentry:intel",
              "source_name": "Intel"
            },
            {
              "download": {
                "details": "",
                "status": "noperm"
              },
              "source": "sentry:intel",
              "source_name": "Intel"
            },
            {
              "download": {
                "status": "notfound"
              },
              "source": "sentry:microsoft",
              "source_name": "Microsoft"
            },
            {
              "download": {
                "status": "notfound"
              },
              "source": "sentry:nvidia",
              "source_name": "NVIDIA"
            },
            {
              "download": {
                "features": {
                  "has_debug_info": false,
                  "has_sources": false,
                  "has_symbols": true,
                  "has_unwind_info": true
                },
                "status": "ok"
              },
              "location": "sentry://project_debug_file/197338063",
              "source": "sentry:project",
              "source_name": "Sentry",
              "unwind": {
                "status": "ok"
              }
            },
            {
              "debug": {
                "status": "ok"
              },
              "download": {
                "features": {
                  "has_debug_info": true,
                  "has_sources": false,
                  "has_symbols": true,
                  "has_unwind_info": false
                },
                "status": "ok"
              },
              "location": "sentry://project_debug_file/197897242",
              "source": "sentry:project",
              "source_name": "Sentry"
            }
          ],
          "debug_status": "found",
          "features": {
            "has_debug_info": true,
            "has_sources": false,
            "has_symbols": true,
            "has_unwind_info": true
          },
          "unwind_status": "found",
          "type": "pe"
        },

The last thing to add could be, in event->exception->values:

    "exception": {
      "values": [
        {
          "type": "EXCEPTION_ACCESS_VIOLATION_READ / 0x0",
          "value": "Fatal Error: EXCEPTION_ACCESS_VIOLATION_READ / 0x0",

It would be nice if the exception type or value could be also printed somewhere, preferably right under the issue name similarly to how Sentry displays it on the summary (issue name itself is usually just crashing function name).

So an idea for stack display could be like this:

tforevive +0x08ea1d   |  test_crash1 (MiscConCommands.cpp:99)
engine    +0x36dcc5   |  sub_18036DCA0
engine    +0x23f2c0   |  Cmd_ExecuteCommand
engine    +0x23da29   |  Cbuf_Execute
[etc...]

Also in case the source code is not available, it would be good to skip the source code context display instead of showing just >undefined: obraz

Again lmk if you'd like to be sent the whole massive payload JSON somewhere and thank you for looking into this.

p0358 avatar Aug 30 '22 20:08 p0358

Gotcha! Yeah if you don't mind emailing the JSON to me so I can test with it that would be wonderful - [email protected]. I'm out traveling right now so I unfortunately won't be able to start on this for about two weeks I think, but can post an update once I start!

IanMitchell avatar Aug 30 '22 21:08 IanMitchell

All right, I've sent the email with the JSON (posting just in case it wouldn't arrive or ended up in spam). There's no rush, happy travelling!

p0358 avatar Aug 30 '22 21:08 p0358

hey @p0358 - I don't think I ever received the email actually. Would you mind resending it?

IanMitchell avatar Sep 20 '22 20:09 IanMitchell

I've re-sent it (there's JSON as attachment, the hookbin link has expired though, but the JSON still has the full event payload)

p0358 avatar Sep 20 '22 20:09 p0358

Hello, any progress on this perchance? :)

p0358 avatar Mar 16 '23 10:03 p0358

Haven't had much time, sorry - been swamped with work. If you're impatient, you can clone this locally and set up an ngrok tunnel to it, and then point Sentry to that tunnel - firing off an event will let you debug what the parser is getting caught up on!

IanMitchell avatar Mar 20 '23 23:03 IanMitchell