Giter Club home page Giter Club logo

Comments (13)

jclark avatar jclark commented on September 14, 2024 1

Represent the snapshot as two arrays

  1. an array of frames
  2. an array of bytes

Frames are:

struct Frame {
     uintptr_t pc;
     // Index into the second array of start of string
     // string is terminated with zero 
     unsigned fileOffset;
     unsigned functionOffset;
     int lineno;
};

Before creating the error object, use libbacktrace to create these two arrays, allocated with malloc/realloc.

Then store the snapshot with the error object in a single allocation from _bal_alloc.

typedef GC struct Error {
    TaggedPtr message;
    uint32_t lineNumber;
    uint32_t nFrames;
    Frame frames[];
} *ErrorPtr;

The second array of bytes follows the last frame. After constructing the error object, free the two arrays.

You should put a limit on the number of frames stored.

from nballerina.

KavinduZoysa avatar KavinduZoysa commented on September 14, 2024

@jclark, I have come up with a couple of approaches.

  1. Use libbacktrace with debug info
    Using this approach we can get the line numbers of the caller functions. But we need to inject debug info into our LLVM IR. We can get an output that is the same as jballerina.

  2. Use libbacktrace without debug info
    This approach is easy to implement but we cannot get the line numbers we can only get the function names.

  3. Pass accumulated location info to each function. (Rust is using something like this just to print the location of the panic)
    In this approach, we need compiler support to identify the location and pass the location to each caller. Also, we have to change the function signature. But we do not need a third-party library.

Since we need to be as lazy as possible as you mentioned above, I prefer to use the second approach. Really appreciate your input on this.

from nballerina.

jclark avatar jclark commented on September 14, 2024

Option 3 is what we are doing now, and it's a pain. It complicates the compiler and requires generating lots of extra basic blocks.

We will at some point need to generate debug info in the IR, because we will need to support debugging. So eventually we want to do 1, but I don't want to do this right now.

Can we just put a little bit of debug info in the IR and get some line numbers out of libbacktrace? For example, if the compiler generates a call to some runtime function foo which may panic, can the compiler just put a line number on that call and get something?

So my question: what is the absolute minimum amount of debug info needed to get some line numbers from libbacktrace?

from nballerina.

KavinduZoysa avatar KavinduZoysa commented on September 14, 2024

@jclark, This is a working example that prints the backtrace for panic https://github.com/KavinduZoysa/test-GCs/tree/backtrace.

In panic-example folder, I have added two LLVM IRs to represent the difference between when there is no debug info and when there is debug info. As a summary, we have to add,

  1. function-related information at function signature (L8)
  2. Line number at each function call(L26)

from nballerina.

KavinduZoysa avatar KavinduZoysa commented on September 14, 2024

@jclark My plan for the implementations is mentioned below. Appreciate your thoughts on this.

  1. Add a new field to ErrorPtr this field should be an array(TaggedPtrArray), each member of this array represents a string that includes method name and line number.
  2. Inside _bal_error_constructor, we have to call libbacktrace and get backtrace info. Using this backtrace info, we have to create stackFrames array. The backtrace info should contain the function name and line number(we have tested that in comment).
  3. Define langlib method called _Berror_stackTrace and it should accept tagged error ptr and return List of stackFrames.

from nballerina.

jclark avatar jclark commented on September 14, 2024

error:stackFrame is not part of the subset: we are a long, long way from having enough of the language implemented to support error:stackFrame.

The key point is what I mentioned at the beginning:

We want to be as lazy as possible here, i.e. do as little work as possible when taking the snapshot, because the backtrace may never be needed

So in _bal_error_construct you should call libbacktrace and, with minimum work, store a compact representation of the stack. Separately, you should have a function to print that representation out (which is what #6 would do).

from nballerina.

jclark avatar jclark commented on September 14, 2024

In the Error structure you should save the pc, lineno, filename and function. Probably best to use a linked list:

struct Frame {
     struct Frame *next
     uintptr_t pc;
     // These can point either into the bytes array
     // or into another frame.
     char *filename;
     char *function;
     int lineno;
     char[] bytes;
}

Search a few frames to avoid having multiple copies of the same filename/function.

from nballerina.

KavinduZoysa avatar KavinduZoysa commented on September 14, 2024

@jclark, I did not understand the use of having char[] bytes;, can you please explain it?

from nballerina.

jclark avatar jclark commented on September 14, 2024

You have to copy the filename and function. Instead of having a separate allocation, you do a single allocation. Similar to MediumString.

from nballerina.

jclark avatar jclark commented on September 14, 2024

Unfortunately what I suggested isn't going to work with garbage allocation. We need to avoid doing any (GC) allocation in the callback.

from nballerina.

KavinduZoysa avatar KavinduZoysa commented on September 14, 2024

If I understand correctly, what we have to do is, allocations done inside the callback function should be copied to a new location (GC allocated) from outside the callback function(May be inside _bal_error_construct). Then we have to free the allocations in callbackfunction. Am I correct?

from nballerina.

jclark avatar jclark commented on September 14, 2024

Note that neither backtrace_create_state nor backtrace_simple do much work. The expensive work (reading all the elf/dwarf structures) happens on the first call to any of backtrace_syminfo/backtrace_pcinfo/backtrace_full.

from nballerina.

jclark avatar jclark commented on September 14, 2024

I guess -funwind is needed because libbacktrace uses the compiler support _Unwind_Backtrace function, which may not get linked in unless -funwind is used.

from nballerina.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.