mortbopet / Ripes

A graphical processor simulator and assembly editor for the RISC-V ISA
https://ripes.me/
MIT License
2.56k stars 274 forks source link

Use debug information to implement a cursor to track execution in C source code #180

Closed lcgamboa closed 2 years ago

lcgamboa commented 2 years ago

It would be nice if there was a cursor in the C source code to follow the execution. I know it is possible to get the relationship between the PC and source code line number from the debug information but I have no idea how difficult it is to implement this.

What we'd be looking at here is to use the DWARF information containing within an ELF file compiled in debug format, and use this as a source mapping. I remember looking into it when i did the compiler support, but put it on hold at the time due to not being able to find a good open-source library that we could depend on for parsing the DWARF information (... and i wasn't in the mood to create one myself!).

Originally posted by @mortbopet in https://github.com/mortbopet/Ripes/issues/162#issuecomment-978976066

lcgamboa commented 2 years ago

I made an initial survey of how to get the line number information. Apparently it is necessary to use a state machine to unpack the information. It's not difficult to implement, but it's complex and will require a lot of study, time and work. I believe that using a library is the best solution for now. The libelfin library seems to be a good option. It can be used to replace the ELFIO library already used by RIPES or we can try to adapt the dwarf class to work with the object of ELFIO class. In the future I will try to implement this support...

mortbopet commented 2 years ago

At first glance, that libelfin library looks good! - if we can use a single library to get both ELF and dwarf info, i'm all for replacing ELFIO. Although it's always an eyesore to see a C++ project nowadays not be a CMake project :weary:.

lcgamboa commented 2 years ago

I saw your cursor implementation for assembly but didn't have time to look at the code. With a little adaptation it is possible to use the libelfin:dwarf++ library with ELFIO, I did this in the example of the libelfin library that maps the lines of code.


#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#define ELFIO_NO_INTTYPES
#endif

#include <elfio/elfio.hpp>
#include <iostream>

#include "libelfin/dwarf/dwarf++.hh"

using namespace ELFIO;

namespace dwarf {

namespace elfio {

template <typename Elfio> class elf_loader : public loader {
  Elfio f;

public:
  elf_loader(const Elfio &file) : f(file) {}

  const void *load(section_type section, size_t *size_out) {
    auto sec = f->sections[elf::section_type_to_name(section)];
    if (sec == nullptr)
      return nullptr;
    *size_out = sec->get_size();
    return sec->get_data();
  }
};

/**
 * Create a DWARF section loader backed by the given ELF
 * file.  This is templatized to eliminate a static dependency
 * between the libelf++ and libdwarf++, though it can only
 * reasonably be used with elf::elf from libelf++.
 */

template <typename Elfio>
std::shared_ptr<elf_loader<Elfio>> create_loader(const Elfio &f) {
  return std::make_shared<elf_loader<Elfio>>(f);
}
}; // namespace elfio
}; // namespace dwarf

void dump_line_table(const dwarf::line_table &lt) {
  for (auto &line : lt) {
    if (line.end_sequence)
      printf("\n");
    else
      printf("%-40s%8d%#20lX\n", line.file->path.c_str(), line.line,
             line.address);
  }
}

int main() {
  elfio reader;

  if (!reader.load("out.elf")) {
    printf("File %s is not found or it is not an ELF file\n", "out.elf");
    return 1;
  }

  dwarf::dwarf dw(dwarf::elfio::create_loader(&reader));

  for (auto cu : dw.compilation_units()) {
    printf("--- <%x>\n", (unsigned int)cu.get_section_offset());
    dump_line_table(cu.get_line_table());
    printf("\n");
  }

  return 0;
}

I believe the result can be used directly in the cursor code.

--- <0>
/tmp/lcd.c                                    50             0X10074
/tmp/lcd.c                                    52             0X10084
/tmp/lcd.c                                    53             0X10088
/tmp/lcd.c                                    54             0X10094
/tmp/lcd.c                                    56             0X1009C
/tmp/lcd.c                                    56             0X100A0
/tmp/lcd.c                                    58             0X100A4
/tmp/lcd.c                                    59             0X100AC
/tmp/lcd.c                                    56             0X100B4
/tmp/lcd.c                                    56             0X100C0
/tmp/lcd.c                                    61             0X100CC
/tmp/lcd.c                                    61             0X100D0

As for cmake support there are some PRs, but apparently the project maintainer has no interest and hasn't updated the project.

I don't know what would be the best way to integrate the dwarf++ library into Ripes, any suggestions?

mortbopet commented 2 years ago

Took your code and implemented it on the c_highlighting branch. cmappings

Added libelfin as a submodule aswell as a wrapper CMakeLists.txt. However, looks like there's still some work to do to get CI to pass on all systems: https://github.com/mortbopet/Ripes/runs/4379698511?check_suite_focus=true

lcgamboa commented 2 years ago

This result is already very good and promising. I'll take a look at the CI problem as soon as I have time. This week I'm out of time.

mortbopet commented 2 years ago

Most of the CI issues were related to libelfin not compiling for windows/MSVC. Decided to create a fork since the upstream seems quite dead, and added preliminary windows support https://github.com/mortbopet/libelfin.

mortbopet commented 2 years ago

Closing this for now since support for this has been merged into main.