mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-09-22 17:20:05 +00:00
Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9086233d89 | ||
|
|
ad1b2fa23d | ||
|
|
f67098f54b | ||
|
|
ea4e671a44 | ||
|
|
0c4c18c64f | ||
|
|
ba9d68951d | ||
|
|
340d504f10 | ||
|
|
e8f03562bf | ||
|
|
c946f88b2a | ||
|
|
56e7bae038 | ||
|
|
c1741b4f39 | ||
|
|
ba41543543 | ||
|
|
5ae60aedc5 | ||
|
|
f23979e20a | ||
|
|
57fb0eb833 | ||
|
|
5d2d13d9d9 | ||
|
|
8342cfbd53 | ||
|
|
2721adf088 | ||
|
|
ffcecd659e | ||
|
|
0ccfb51102 | ||
|
|
f41f8dd719 | ||
|
|
fa6b04ab24 | ||
|
|
156615a3ba | ||
|
|
57a35c6b28 | ||
|
|
76ce8a79c2 | ||
|
|
932db741fa | ||
|
|
0add3ef72c | ||
|
|
e417a96ce4 | ||
|
|
b2262532a7 | ||
|
|
b24326f502 | ||
|
|
66c1fa337c |
@@ -1,8 +0,0 @@
|
||||
*.sh text=auto eol=lf
|
||||
src/lib_rdi/rdi.h text=auto eol=lf
|
||||
src/lib_rdi/rdi.c text=auto eol=lf
|
||||
*.meta.* text=auto eol=lf
|
||||
*.exe filter=lfs diff=lfs merge=lfs -text
|
||||
*.pdb filter=lfs diff=lfs merge=lfs -text
|
||||
*.elf filter=lfs diff=lfs merge=lfs -text
|
||||
*.rdi filter=lfs diff=lfs merge=lfs -text
|
||||
+11
-132
@@ -8,144 +8,23 @@ on:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
env:
|
||||
TORTURE_FLAGS: "--gen_crash_dump"
|
||||
|
||||
jobs:
|
||||
build-ubuntu-24-04:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- raddbg
|
||||
- raddbg_non_graphical
|
||||
- radlink
|
||||
- radbin
|
||||
- torture
|
||||
compiler:
|
||||
- clang-22
|
||||
# TODO: - gcc
|
||||
mode:
|
||||
- debug
|
||||
- release
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
lfs: true
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-add-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-22 main"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-22 lld-22 llvm-22 gcc-14 pkg-config libfreetype6-dev libx11-dev libxext-dev libgl1-mesa-dev libegl1-mesa-dev
|
||||
sudo ln -s /usr/bin/llvm-symbolizer-22 /usr/bin/llvm-symbolizer
|
||||
- name: build
|
||||
shell: bash
|
||||
run: |
|
||||
CC="${{ matrix.compiler }}" ./build.sh "${{ matrix.target }}" "${{ matrix.mode }}"
|
||||
|
||||
build-windows-2022:
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- raddbg
|
||||
- radlink
|
||||
- radbin
|
||||
- mule_main
|
||||
compiler:
|
||||
- msvc
|
||||
- clang
|
||||
mode:
|
||||
- debug
|
||||
- release
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
lfs: true
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: build (vs 2022)
|
||||
shell: cmd
|
||||
run: |
|
||||
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VS_INSTALL=%%i"
|
||||
call "%VS_INSTALL%\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
call build meta "${{ matrix.target }}" "${{ matrix.compiler }}" "${{ matrix.mode }}" || exit /b 1
|
||||
|
||||
run-ubuntu-24-04:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler:
|
||||
- clang-22
|
||||
# TODO: - gcc
|
||||
mode:
|
||||
- debug
|
||||
# TODO: - release
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
lfs: true
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-add-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-22 main"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-22 lld-22 llvm-22 gcc-14 pkg-config libfreetype6-dev libx11-dev libxext-dev libgl1-mesa-dev libegl1-mesa-dev
|
||||
# crash callstack symbolizer depends on LLVM
|
||||
sudo ln -s /usr/bin/llvm-symbolizer-22 /usr/bin/llvm-symbolizer
|
||||
- name: Run
|
||||
shell: bash
|
||||
run: |
|
||||
CC="${{ matrix.compiler }}" ./run_tests.sh clang "${{ matrix.mode }}" -- \* $TORTURE_FLAGS
|
||||
- name: Upload Artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: torture-crash-dumps-${{ matrix.compiler }}-${{ matrix.mode }}
|
||||
path: |
|
||||
build/*.core
|
||||
build/*.exe
|
||||
build/*.pdb
|
||||
build/torture_artifacts/**
|
||||
if-no-files-found: ignore
|
||||
|
||||
run-windows-2022:
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler:
|
||||
- msvc
|
||||
- clang
|
||||
mode:
|
||||
- debug
|
||||
- release
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
lfs: true
|
||||
- name: Run
|
||||
shell: cmd
|
||||
run: |
|
||||
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VS_INSTALL=%%i"
|
||||
call "%VS_INSTALL%\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
call run_tests.bat ${{ matrix.compiler }} ${{ matrix.mode }} -- * %TORTURE_FLAGS% || exit /b 1
|
||||
- name: Upload Artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: torture-crash-dumps-${{ matrix.compiler }}-${{ matrix.mode }}
|
||||
path: |
|
||||
build/*.dmp
|
||||
build/*.exe
|
||||
build/*.pdb
|
||||
build/torture_artifacts/**
|
||||
if-no-files-found: ignore
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
call build raddbg msvc debug || exit /b 1
|
||||
call build raddbgi_from_pdb msvc debug || exit /b 1
|
||||
call build raddbgi_from_dwarf msvc debug || exit /b 1
|
||||
call build raddbgi_dump msvc debug || exit /b 1
|
||||
call build raddbg clang debug || exit /b 1
|
||||
call build raddbgi_from_pdb clang debug || exit /b 1
|
||||
call build raddbgi_from_dwarf clang debug || exit /b 1
|
||||
call build raddbgi_dump clang debug || exit /b 1
|
||||
|
||||
+3
-5
@@ -1,5 +1,3 @@
|
||||
/build/
|
||||
/local/
|
||||
*~
|
||||
.vs
|
||||
tags
|
||||
/build/
|
||||
/local/
|
||||
*~
|
||||
|
||||
-399
@@ -1,399 +0,0 @@
|
||||
# v0.9.28-alpha
|
||||
|
||||
## Linker Changes
|
||||
- Implemented `/OPT:ICF` identical COMDAT folding.
|
||||
- Added DEF, and SECTION configuration support.
|
||||
- Improved COMDAT handling for weak symbols, ICF, and zero-sized COMDAT
|
||||
anchors, and stopped emitting empty marker sections that could produce invalid
|
||||
PE images.
|
||||
- Fixed import/export edge cases, including import data-directory bounds and
|
||||
archive member handling for `__imp_*` symbols.
|
||||
- Accepted duplicate identical weak search aliases without reporting multiply
|
||||
defined symbols.
|
||||
- Add support for UTF-16 encoded response files.
|
||||
- Fixed DBI section-contribution image-section range mapping.
|
||||
- Improved debug logging for `/OPT:REF` liveness stats and unresolved symbols
|
||||
referenced by `.llvm_addrsig`.
|
||||
|
||||
# v0.9.27-alpha
|
||||
|
||||
## Debugger Changes
|
||||
|
||||
- Fixed an issue which would rarely cause steps to take a long time.
|
||||
- Drastically improved memory usage of PDB -> RDI conversion for some
|
||||
workloads, notably PDBs with many small compilation units.
|
||||
- Added the ability to disambiguate symbol names by module name, debug info
|
||||
name, or compilation unit name in expressions. This can be done using the
|
||||
usual syntax, e.g. `my_module.dll!foo`, or `my_unit.obj!foo`. `:` can also be
|
||||
used instead of `!`.
|
||||
- Added the ability to redirect debug string output to any label on a
|
||||
per-target basis. Those labels can then be evaluated in any watch window or
|
||||
text visualizer. By default, all targets write to `output`, which is
|
||||
automatically visualized by an `Output` tab. But if you want different
|
||||
targets to route their debug output to different buffers, you can name the
|
||||
buffers differently, and then view those in separate text views.
|
||||
- Added a `Run To Name` command.
|
||||
- Added an `Auto Load Last Project` setting, which will automatically load the
|
||||
most recently loaded project on startup (the old behavior pre-0.9.26). This
|
||||
setting is off by default.
|
||||
- Added right-click menus for watch expressions, to easily find them in a
|
||||
memory view, or to break when their value changes.
|
||||
- Added preliminary support for symbol servers. First, the debugger can attempt
|
||||
to download debug info for any module via the `Download Module Debug Info`
|
||||
command (also accessible by right-clicking a module). Second, the debugger
|
||||
can be configured to automatically do this for all modules which don't have
|
||||
debug info via the `Auto Download Debug Info` setting. The local cache path
|
||||
and server URLs used by the debugger for downloading symbols are parsed from
|
||||
the `_NT_SYMBOL_PATH` environment variable. If that environment variable is
|
||||
not configured, then it falls back to the defaults of `C:/SymbolCache` and
|
||||
`https://msdl.microsoft.com/download/symbols`.
|
||||
- Adjusted the default dark theme.
|
||||
- Made several UI visual improvements.
|
||||
- Fixed a few issues relating to offline symbol evaluation (evaluation of
|
||||
symbols in debug info when not debugging); this was sometimes preventing
|
||||
function listers (e.g. used in `Go To Name`) from working when not debugging.
|
||||
- Fixed the debugger's snap-to-code-location rule when a `Disassembly` tab is
|
||||
occupying the same panel as source code tabs. Previously, the debugger would
|
||||
overly aggressively switch back to source code tabs. Now, it should respect
|
||||
the preference of disassembly correctly.
|
||||
- Improved the stability of the step-over-line operation. Our stepping
|
||||
algorithm would sometimes cause spurious (and rarely, non-spurious)
|
||||
access violation exceptions when a thread would try to execute at address
|
||||
`0x38f`. This most regularly surfaced when an exception on one thread (like a
|
||||
breakpoint being hit) would interrupt stepping on another thread. This has
|
||||
been resolved, and stepping in such scenarios should be much more reliable.
|
||||
- Fixed some crashes in the PDB -> RDI converter. (#792, #803)
|
||||
- Fixed a bug which was sometimes causing a step-over operation to resume the
|
||||
process without catching the target thread after its step completed. (#804)
|
||||
- Adjusted evaluation visualizations to include fully qualified symbol names.
|
||||
(#809)
|
||||
|
||||
## Linker Changes
|
||||
- /OPT:REF Fixed a race in the dead-code-elimination pass where the linker
|
||||
sometimes erroneously removed live sections.
|
||||
- Fixed a bug where the linker emitted relocations for empty COFF resource entries,
|
||||
which lead to "relocating against symbol that is in a removed section" errors.
|
||||
- Added the /RAD_TYPE_SERVER switch to run the type-dedup pass on object files,
|
||||
producing RRT files that can be passed back on the next link to improve
|
||||
linking speed.
|
||||
|
||||
# v0.9.26-alpha
|
||||
|
||||
## Debugger Changes
|
||||
|
||||
- Added scope ending visualizations to the text view, to display the header of
|
||||
a scope at the closing position of the scope. This can be turned off via the
|
||||
`Cursor Scope End Annotations` setting.
|
||||
- Added the ability to evaluate `autos`, which is a collection of expressions
|
||||
that the debugger determines refer to symbols which are being (or about to
|
||||
be) changed by the selected thread.
|
||||
- Added source-inline and disassembly-inline visualization of auto expressions.
|
||||
This can be turned off via the `Show Auto Watches In Source / Disassembly`
|
||||
setting.
|
||||
- Added the ability to evaluate local static variables unambiguously, when the
|
||||
same name is used for multiple local static variables, even when outside the
|
||||
function containing the local static. This can be done by namespacing the
|
||||
variable name with the containing function, either using a `::` or `.`
|
||||
operator. (#359)
|
||||
- Added a setting, `Transient Tabs`, to disable transient tabs being opened,
|
||||
when the debugger automatically snaps to new code locations.
|
||||
- Added a setting, `Display Pointer Addresses Before Contents`, to always
|
||||
force the debugger to show pointer addresses before attempting to visualize
|
||||
to what the pointer points.
|
||||
- Adjusted the `Step Out` command to perform scope-granularity stepping, rather
|
||||
than only being function-frame-granularity. This uses scope information found
|
||||
in debug info to step out of scopes when possible, rather than always exiting
|
||||
the current function frame. This makes the command much more useful for
|
||||
stepping through larger functions with several scopes of larger work, or for
|
||||
stepping out of loops. (#746)
|
||||
- Adjusted the debugger's user & project configuration file behavior to be less
|
||||
idiosyncratic with other programs. Project files are no longer auto-loaded
|
||||
based on the project last loaded during the previous debugger runtime.
|
||||
Creating a new user or project also does not immediately require choosing a
|
||||
path for the file; these can later be specified using the respective `Save`
|
||||
commands. (#743)
|
||||
- Adjusted the debugger to automatically set the current path when loading a
|
||||
project file. (#743)
|
||||
- Added syntax highlighting and tab-completion for view identifiers in
|
||||
watch expressions.
|
||||
- Added more in-debugger documentation for views and their arguments, when
|
||||
written in a watch expression.
|
||||
- Moved configuration data previously treated as project data to being user
|
||||
data, tagged with the associated project. This includes loaded debug info and
|
||||
breakpoints. The effect of this is that project files are much easier to
|
||||
check into source control (they do not change in spurious ways for other
|
||||
users on a project), but project-related user configuration will still be
|
||||
correctly filtered based on whatever project is loaded.
|
||||
- Improved the debugger's behavior with respect to keeping previously-loaded
|
||||
debug info around. Now, when the debugger begins a new debugging session, it
|
||||
will unload all loaded debug info, and it will only reload debug info that is
|
||||
actively found to be needed by debuggees. This preserves the debugger's
|
||||
ability to use debug info without debugging for features like
|
||||
go-to-definition and type evaluation, but it stops the debugger from keeping
|
||||
old and unneeded debug info loaded indefinitely (until it is manually
|
||||
unloaded). (#711)
|
||||
- Adjusted recent project visualization to include the name of the project,
|
||||
specified with the `Project Name` setting.
|
||||
- Renamed `Switch` to `Open Source File From Debug Info`, to better represent
|
||||
the command's functionality.
|
||||
- Adjusted file path evaluation visualization to include the full path to the
|
||||
containing folder, when it is not obvious from context (for instance, if the
|
||||
file evaluation is a child evaluation of a folder evaluation, the containing
|
||||
folder path is not shown; if the file path is evaluated standalone, as it is
|
||||
in the lister generated for the `Open Source File From Debug Info` command,
|
||||
then the containing folder path is shown).
|
||||
- Improved visualization of unmapped addresses in the memory view. Now, instead
|
||||
of bytes at these addresses being displayed as `0` with a special background,
|
||||
they are displayed as `x`, and the annotations for these addresses (displayed
|
||||
at the bottom of the memory view, for the cursor's cell) explicitly label
|
||||
them as being unmapped.
|
||||
- Improved visualization of unmapped addresses in watch expressions. Now,
|
||||
instead of only being colored differently with evaluations reading as zeroed
|
||||
memory, the value will explicitly label addresses as unmapped when possible.
|
||||
- Fixed a leak which most notably manifested during rapid creation/destruction
|
||||
of new threads. (#780)
|
||||
- Fixed a leak related to debugger's config state. This was difficult to
|
||||
reproduce, but it may fix leaks experienced after running the debugger for an
|
||||
extended period of time.
|
||||
- Fixed a bug where cross-module evaluations would resolve to incorrect
|
||||
addresses, due to using the incorrect module's base virtual address. This was
|
||||
also causing broken behavior sometimes in the `Go To Name` lister, and
|
||||
other similar UIs.
|
||||
- Fixed a bug where panels without tabs were unnecessarily keeping the debugger
|
||||
UI awake in some cases.
|
||||
- Fixed a bug preventing hover evaluation from working on indexing expressions.
|
||||
(#707)
|
||||
- Replaced the DWARF -> RDI converter with a new multithreaded version, which
|
||||
should execute much faster on larger DWARF debug info, and produce much
|
||||
smaller RDI output, due to the addition of type deduplication.
|
||||
- Introduced structured namespaces to RDI, which structurally encode namespace
|
||||
hierarchies for debug info symbols, instead of this information being
|
||||
implicitly baked using language-specific conventions into symbol names. This
|
||||
is used to deduplicate repeated namespace prefixes from symbol names, leading
|
||||
to a smaller string footprint for generated RDIs. Symbols now only store
|
||||
their partially-qualified name. For example, for a C++ symbol `A::B::C`, the
|
||||
symbol itself will store its name as `C`, but it will also record its
|
||||
container as being `B`. `B`, then, will record its container as being `A`.
|
||||
This allows users of RDI to dynamically construct the fully-qualified name as
|
||||
needed, and it allows for more reliable and language-agnostic usage of
|
||||
partially-qualified symbol names.
|
||||
- Adjusted the debugger to not rely on `SynchronizationBarrier` functions on
|
||||
Windows when they're not available (pre-Windows-8). (#739)
|
||||
|
||||
|
||||
## Linker Changes
|
||||
|
||||
- Implemented /DEBUG:GHASH
|
||||
- Fixed an issue where an import thunk or import address could be linked from
|
||||
the currently searched library instead of the library where the first import
|
||||
reference was found. For example, if the first pass finds the import thunk in
|
||||
foo.lib and the second pass resolves symbol to the import address in bar.lib,
|
||||
the linker now always linkes the import thunk and import address members from
|
||||
the library where the first reference was found.
|
||||
- /OPT:REF: Fixed an issue with garbage collection of associated sections and
|
||||
weak symbols that led linker to remove live sections.
|
||||
- /OPT:REF Added relocation batching to reduce contention on the global
|
||||
reference list during garbage collection.
|
||||
|
||||
# v0.9.25-alpha
|
||||
|
||||
## Debugger Changes
|
||||
|
||||
- Added a dedicated cursor address bar to the memory view. This can be focused
|
||||
with the keyboard with the `Focus Menu` command. By default, this command is
|
||||
bound to `Alt + D`, but if you have existing configuration, you may need to
|
||||
bind it yourself manually (this can be done in the palette). This address bar
|
||||
accepts any expression, and controls the cursor's address. All other
|
||||
configuration options are still available in the memory view settings.
|
||||
- Added "Peek Types" to the memory view. These control the set of types which
|
||||
are used to interpret bytes at the cursor. Options exist for basic cases,
|
||||
like integers and floats, but the view also supports entering a list of
|
||||
full type expressions, which can include user-defined types like structures.
|
||||
- Added annotations for members of structure variables to the memory view.
|
||||
- Added a dedicated zoom setting for the memory view.
|
||||
- Added a setting for automatically determining the number of columns in the
|
||||
memory view based on the available space.
|
||||
- Fixed many bugs and improved visuals in the memory view. (#690)
|
||||
- Fixed the debugger incorrectly evaluating pointer casts of registers in
|
||||
register space, rather than promoting them to process address space
|
||||
evaluations. This fixes cases where expressions like `(int *)rax` were not
|
||||
evaluating correctly. (#659)
|
||||
- Fixed the debugger incorrectly requiring multiple step operations when source
|
||||
lines mapped to multiple discontiguous ranges of instructions. This most
|
||||
notably showed up with certain styles of `for`-loops, especially with Clang
|
||||
and other non-C/C++ languages, and function prologues in non-C/C++ languages.
|
||||
- Fixed the debugger treating the `foo, x` fastpath for `hex(foo)` differently
|
||||
than `hex(foo)`, in that the hexadecimal setting was not being inherited.
|
||||
- Fixed the debugger leaking debug info conversion process handles. This
|
||||
addresses cases where the debugger was preventing stale debug info from being
|
||||
updated, after a rebuild. (#736)
|
||||
- Fixed the debugger not correctly using target-embedded markup data. (#702)
|
||||
- Fixed the debugger not showing full call stacks when the top instruction
|
||||
pointer is located at a 0 address.
|
||||
- Fixed integer casts producing incorrect values. (#754)
|
||||
- Fixed the PDB -> RDI converter's interpretation of certain empty argument
|
||||
lists in function types in PDB. This fixes some cases where local variables
|
||||
were being interpreted as parameters, which resulted in incorrect evaluation
|
||||
results. (#751)
|
||||
- Fixed several crashes and instabilities. (#735)
|
||||
- Added a dedicated settings button to the focused tab. This opens the same
|
||||
menu as right-clicking the tab, but this makes this menu (and thus all
|
||||
options for all tabs and views) more discoverable.
|
||||
- Added optional cursor trails, to visualize cursor motion. Can be turned off
|
||||
by toggling the `Cursor Trail` setting.
|
||||
- Added project names. When set, this name is used for displaying the project
|
||||
instead of the project filename in the UI and window titles.
|
||||
- Adjusted the rules for explicit file location finding commands (like
|
||||
`Switch`, or `Switch To Partner File`), such that they prefer creating new
|
||||
non-transient tabs, even if the target file is found within an existing
|
||||
transient tab.
|
||||
- Adjusted the source code context menu to activate commands with a single
|
||||
press, rather than the usual double click behavior from general palettes.
|
||||
|
||||
## Linker Changes
|
||||
|
||||
- Optimized debug info generation to reduce memory usage in large links by 25%.
|
||||
- Fixed a determinism issue that caused linker to write unreliable debug info.
|
||||
- Fixed over-reservetaion of PDB pages, reducing the PDB size.
|
||||
- Implemented /INFERASANLIBS
|
||||
- Implemented /WHOLEARCHIVE
|
||||
- Implemented /PDBSTRIPPED
|
||||
- Set correct defaults for unload and bind import tables.
|
||||
|
||||
# v0.9.24-alpha
|
||||
|
||||
## Debugger Changes
|
||||
|
||||
- Added the ability for the debugger to load, use, and evaluate using debug
|
||||
info, even when not actively debugging. The debugger will now keep a process'
|
||||
debug info loaded, even after the process ends. It stores the set of loaded
|
||||
debug info files in the project configuration file, meaning it will also
|
||||
automatically load the same debug info across many runs. Debug info can also
|
||||
be loaded manually (without ever launching a process) with the
|
||||
`Load Debug Info` command. There is also a new tab, `Debug Info`, which allows
|
||||
viewing and managing the set of loaded debug info files.
|
||||
- Improved the debugger's behavior when used as a drag & drop target, to allow
|
||||
for debug info loading as an option (when relevant), and to better handle the
|
||||
case where many files (potentially of different types) are dropped together.
|
||||
- Improved debug info searching performance and reponsiveness in large projects.
|
||||
- Fixed some crashes and incorrect results with the new `list` view.
|
||||
- Fixed some cases where RDIs did not contain some basic types from their
|
||||
originating PDBs.
|
||||
- Allowed `.` and `->` operators to be used with array types.
|
||||
- Fixed the debugger's treatment of quoted command line arguments when building
|
||||
targets. In previous versions, calling `raddbg main.exe "foo bar baz"` would
|
||||
create a target `main.exe` with arguments `foo bar baz` (dropping the quotes).
|
||||
This is now fixed, such that the target's arguments string will also contain
|
||||
the quotes, and pass them to the target when launched.
|
||||
- Fixed the debugger not correctly responding (through font and UI scale) to DPI
|
||||
changes.
|
||||
- Fixed the debugger incorrectly generating conflicting source line info records
|
||||
in PDB -> RDI conversion, which in some scenarios was preventing source line
|
||||
maps from working (leading to breakpoint resolution failing).
|
||||
- Other small fixes, improvements, and tweaks.
|
||||
|
||||
# v0.9.23-alpha
|
||||
|
||||
## Debugger Changes
|
||||
|
||||
- Further improved PDB -> RDI conversion performance & memory usage.
|
||||
- Adjusted limits for the amount of PDB -> RDI conversion work that the
|
||||
debugger will kick off, to prevent PDB -> RDI conversion interfering with
|
||||
debuggee performance.
|
||||
- Reintroduced the `list` lens, which gathers all nodes in a linked list, and
|
||||
visualizes them as a flat list of pointers (the same as how an array of
|
||||
pointers is visualized).
|
||||
- Fixed a crash when closing empty Geometry 3D views. (#652, #657)
|
||||
- Fixed the debugger not visualizing `enum` types when evaluated through a
|
||||
bitfield type. (#655)
|
||||
- Fixed the PDB -> RDI conversion not correctly generating location information
|
||||
for function parameters, when the EXE/PDB were built to include support for
|
||||
Edit and Continue (`-ZI` switch). (#656)
|
||||
|
||||
## Binary Utility Changes
|
||||
|
||||
- Fixed a crash when using the `--compress` option, when generating RDI files.
|
||||
|
||||
# v0.9.22-alpha
|
||||
|
||||
## Debugger Changes
|
||||
|
||||
- Further improved PDB -> RDI conversion performance.
|
||||
- Capped the number of additional threads / processes spawned for PDB -> RDI
|
||||
conversion.
|
||||
- Prioritize PDB -> RDI conversion based on what is actually found to be
|
||||
necessary by the debugger, rather than converting all PDBs in the order in
|
||||
which they're discovered.
|
||||
- Added preliminary support for DWARF -> RDI conversion on Windows.
|
||||
- The debugger now relies on source file checksums to determine whether or not
|
||||
a source file is out-of-date with respect to what was compiled when debug info
|
||||
was produced, rather than just the modification timestamp.
|
||||
- The debugger now will rely on debug info to detect the language of source code
|
||||
files, if it cannot infer from the source file's extension, or view settings.
|
||||
This will enable features like syntax highlighting and hover evaluation in
|
||||
cases like `.inl` files being included in C++ projects.
|
||||
- The debugger now will restore the last focused window when continuing
|
||||
automatically. (#245, #596)
|
||||
- Watch tables have been simplified in that they no longer have a separate
|
||||
column for evaluation types, since this was usually taking a lot more space
|
||||
than it deserved. The type of evaluations is still displayed in watch table
|
||||
cells, and it can always be evaluated directly via `typeof`.
|
||||
- Type evaluations have been simplified in watch tables as well; they no longer
|
||||
have untitled columns for sizes and offsets, this is instead displayed as
|
||||
an extra note by default. Similar behavior to the original behavior can still
|
||||
be obtained using the `columns` view, if needed.
|
||||
- The debugger no longer uses complex `union` types for most registers, and
|
||||
instead just displays the register value plainly.
|
||||
- The hover evaluation UI has been made larger when needed.
|
||||
- The debugger now prefers matching global, function, and type identifiers to
|
||||
the most relevant debug info and module in context; this fixes evaluation in
|
||||
some multi-process debugging contexts. (#581)
|
||||
- Fixed the debugger unnecessarily stripping `enum` type information when
|
||||
accessed through array operators. (#634)
|
||||
- The debugger now understands a standalone `unsigned` keyword as an
|
||||
`unsigned int` type, to match C rules.
|
||||
- The debugger now uses the current working directory to form the working
|
||||
directory for targets specified on the command line, to match behavior when
|
||||
running a command from the command line without the debugger.
|
||||
- Improved call stack computation performance.
|
||||
- Improved debugger memory usage over long periods of time.
|
||||
- Fixed string-pointer comparison not working with not-equal (`!=`) operations.
|
||||
- Fixed a bug which was causing bad debuggee performance on some threads after
|
||||
some interactions with the debugger controller.
|
||||
- Fixed incorrect results when adding two register values. (#642)
|
||||
- Fixed the interpretation of register expressions in visualizers. (#649)
|
||||
- Fixed "forever loading" states in disassembly views in some cases. (#643)
|
||||
- Fixed jittering on window resizing. (#636)
|
||||
- Fixed the bitmap visualizer crashing in some circumstances relating to
|
||||
unsupported bitmap sizes. (#444, #563)
|
||||
- Fixed a crash when an empty `cast()` expression would be evaluated. (#625)
|
||||
- Fixed a crash when an invalid expression would be visualized using the `text`
|
||||
view. (#647)
|
||||
|
||||
## Linker Changes
|
||||
|
||||
- Changed symbol resolution in libaries to match MSVC behavior.
|
||||
- Optimized image building step to reduce memory usage.
|
||||
- Linker memory maps all input files by default to lower memory usage.
|
||||
(`/RAD_MEMORY_MAP_FILES`)
|
||||
- If debug info is available, linker uses it to show file and line number for
|
||||
unresolved relocations.
|
||||
- Improved base relocation build performance for large images, cutting build
|
||||
time by 70%.
|
||||
- Added stubs for `/Brepro`, `/D2`, and /ErrorReport to improve compatability
|
||||
with existing response files
|
||||
- Implemented section garbage collection (`/OPT:REF`)
|
||||
- Fixed bug where thread local variables pointed to incorrect types.
|
||||
- Changed rules for weak and undefined symbols, now weak symbol is not allowed
|
||||
to replace an undefined symbol.
|
||||
- Linker no longer creates thunks for imports that don't require them.
|
||||
|
||||
## Binary Utility Changes
|
||||
|
||||
- The binary utility, like the debugger, now can convert DWARF debug info to
|
||||
RDI files. When both DWARF and PDB info is present, it can now convert both,
|
||||
and produce a single final RDI file with all information.
|
||||
- Textual dumping of RDI files is now done in parallel, massively improving
|
||||
dumping performance.
|
||||
- PDB -> Breakpad conversion performance has now been parallelized to a greater
|
||||
degree, improving performance.
|
||||
@@ -1,19 +1,19 @@
|
||||
Copyright (c) Epic Games Tools
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Copyright (c) 2024 Epic Games Tools
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,428 +1,327 @@
|
||||
# The RAD Debugger Project
|
||||
|
||||
_**NOTE:** This README does not document usage instructions and tips for the
|
||||
debugger itself, and is intended as a technical overview of the project. The
|
||||
debugger's README, which includes usage instructions and tips, can be found
|
||||
packaged along with debugger releases, or within the `build` folder after a
|
||||
local copy has been built. You can find pre-built release binaries
|
||||
[here](https://github.com/EpicGames/raddebugger/releases)._
|
||||
|
||||
The RAD Debugger is a native, user-mode, multi-process, graphical debugger. It
|
||||
currently only supports local-machine Windows x64 debugging with PDBs, with
|
||||
plans to expand and port in the future. In the future we'll expand to also
|
||||
support native Linux debugging and DWARF debug info.
|
||||
|
||||
The debugger is currently in *ALPHA*. In order to get the debugger
|
||||
bullet-proof, it'd greatly help out if you submitted the issues you find
|
||||
[here](https://github.com/EpicGames/raddebugger/issues), along with any
|
||||
information you can gather, like dump files (along with the build you used),
|
||||
instructions to reproduce, test executables, and so on.
|
||||
|
||||
In addition to the debugger, we aim to further improve the toolchain with two
|
||||
additional related technologies: **(1)** the RAD Debug Info (RDI) format, and
|
||||
**(2)** the RAD Linker.
|
||||
|
||||
## The RAD Debug Info (RDI) Format
|
||||
|
||||
The RAD Debug Info (RDI) format is our custom debug information format, which
|
||||
the debugger parses and uses, rather than the debug information natively
|
||||
produced by toolchains, like PDB or DWARF. To work with these existing
|
||||
toolchains, we convert PDB (and eventually PE/ELF files with embedded DWARF)
|
||||
into the RDI format on-demand.
|
||||
|
||||
The RDI format is currently specified in code, in the files within the
|
||||
`src/lib_rdi` folder. In [`rdi.h`](src/lib_rdi/rdi.h) and
|
||||
[`rdi.c`](src/lib_rdi/rdi.c), the types and functions which define the format
|
||||
itself are specified. In [`rdi_parse.h`](src/lib_rdi/rdi_parse.h) and
|
||||
[`rdi_parse.c`](src/lib_rdi/rdi_parse.c), helpers for parsing the format are
|
||||
included.
|
||||
|
||||
We also have an in-progress library for constructing and serializing RDI data,
|
||||
located within the `src/lib_rdi_make` folder.
|
||||
|
||||
Our `radbin` utility (accessible through the debugger too, via the `--bin`
|
||||
command line argument) is capable of converting native debug information formats
|
||||
to RDI, and of producing textual dumps of contents stored within RDI files.
|
||||
|
||||
## The RAD Linker
|
||||
|
||||
The RAD Linker is a new performance linker for generating x64 PE/COFF binaries.
|
||||
It is designed to be very fast when creating gigantic executables. It generates
|
||||
standard PDB files for debugging, but it can also (optionally) natively create
|
||||
RAD Debug Info too, which is useful both to eliminate on-demand conversion time
|
||||
when debugging, but also for huge executables that otherwise create broken
|
||||
PDBs that overflow internal 32-bit tables.
|
||||
|
||||
The RAD Linker is primarily optimized to handle huge linking projects. In our
|
||||
test cases (where debug info is multiple gigabytes), we see 50% faster link
|
||||
times.
|
||||
|
||||
The command line syntax is fully compatible with MSVC; you can get a full list
|
||||
of implemented switches from `/help`.
|
||||
|
||||
Our current designed-for use case for the linker is to help with the
|
||||
compile-debug cycle of huge projects. We don't yet have support for
|
||||
link-time-optimizations, but this feature is on the road map.
|
||||
|
||||
By default, the linker spawns as many threads as there are cores, so if you plan
|
||||
to run multiple linkers in parallel, you can limit the number of thread workers
|
||||
via `/rad_workers`.
|
||||
|
||||
We also have support for large memory pages, which, when enabled, reduce link
|
||||
time by another 25%. To link with large pages, you need to explicitly request
|
||||
them via `/rad_large_pages`. Large pages are off by default, since Windows
|
||||
support for large pages is a bit buggy; we recommend they only be used in Docker
|
||||
or VM images where the environment is reset after each link. In a standard
|
||||
Windows environment, using large pages otherwise will fragment memory quickly,
|
||||
forcing a reboot. We are working on a Linux port of the linker that will be able
|
||||
to build with large pages robustly.
|
||||
|
||||
A benchmark of the linker's performance is below:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Project Development Setup Instructions
|
||||
|
||||
**NOTE: Currently, only x64 Windows development is supported for the project.**
|
||||
|
||||
## 1. Installing the Required Tools (MSVC & Windows SDK)
|
||||
|
||||
In order to work with the codebase, you'll need the [Microsoft C/C++ Build Tools
|
||||
v15 (2017) or later](https://aka.ms/vs/17/release/vs_BuildTools.exe), for both
|
||||
the Windows SDK and the MSVC compiler and linker.
|
||||
|
||||
If the Windows SDK is installed (e.g. via installation of the Microsoft C/C++
|
||||
Build Tools), you may also build with [Clang](https://releases.llvm.org/).
|
||||
|
||||
## 2. Build Environment Setup
|
||||
|
||||
Building the codebase can be done in a terminal which is equipped with the
|
||||
ability to call either MSVC or Clang from command line.
|
||||
|
||||
This is generally done by calling `vcvarsall.bat x64`, which is included in the
|
||||
Microsoft C/C++ Build Tools. This script is automatically called by the `x64
|
||||
Native Tools Command Prompt for VS <year>` variant of the vanilla `cmd.exe`. If
|
||||
you've installed the build tools, this command prompt may be easily located by
|
||||
searching for `Native` from the Windows Start Menu search.
|
||||
|
||||
You can ensure that the MSVC compiler is accessible from your command line by
|
||||
running:
|
||||
|
||||
```
|
||||
cl
|
||||
```
|
||||
|
||||
If everything is set up correctly, you should have output very similar to the
|
||||
following:
|
||||
|
||||
```
|
||||
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30151 for x64
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
usage: cl [ option... ] filename... [ /link linkoption... ]
|
||||
```
|
||||
|
||||
### 3. Building
|
||||
|
||||
Within this terminal, `cd` to the root directory of the codebase, and just run
|
||||
the `build.bat` script:
|
||||
|
||||
```
|
||||
build
|
||||
```
|
||||
|
||||
You should see the following output:
|
||||
|
||||
```
|
||||
[debug mode]
|
||||
[msvc compile]
|
||||
[default mode, assuming `raddbg` build]
|
||||
metagen_main.c
|
||||
searching C:\devel\raddebugger/src... 458 files found
|
||||
parsing metadesk... 16 metadesk files parsed
|
||||
gathering tables... 97 tables found
|
||||
generating layer code...
|
||||
raddbg_main.c
|
||||
```
|
||||
|
||||
If everything worked correctly, there will be a `build` folder in the root
|
||||
level of the codebase, and it will contain a freshly-built `raddbg.exe`.
|
||||
|
||||
This `raddbg.exe` will have been built in **debug mode**, which is not built
|
||||
with optimizations, and may perform worse. To produce a
|
||||
**release mode executable**, run `build.bat` with a `release` argument:
|
||||
|
||||
```
|
||||
build release
|
||||
```
|
||||
|
||||
This build will take significantly longer.
|
||||
|
||||
By default, `build.bat` only builds the debugger if no arguments (or just
|
||||
`release`) are passed, but additional arguments can be passed to build the RAD
|
||||
Linker, or the `radbin` CLI binary file utility:
|
||||
|
||||
```
|
||||
build radlink release
|
||||
build radbin release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Project Roadmap
|
||||
|
||||
### The Initial Alpha Battle-Testing Phase
|
||||
|
||||
The first priority for the project is to ensure that the most crucial components
|
||||
are functioning extremely reliably for local, x64, Windows development.
|
||||
For the debugger, this would include parts like debug info conversion, debug
|
||||
info loading, process control, stepping, evaluation (correct usage of both
|
||||
location info and type info), and a robust frontend which ensures the lower
|
||||
level parts are usable. For the linker, this is a matter of reliability and
|
||||
convergence with existing linker behavior.
|
||||
|
||||
We feel that we've already come a long way in all of these respects, but given
|
||||
the massive set of possible combinations of languages, build settings,
|
||||
toolchains, used language features, and patterns of generated code, we still
|
||||
expect some issues, and are prioritizing these issues being resolved first.
|
||||
|
||||
We also hope to continue to improve performance in this phase. For the debugger,
|
||||
this primarily includes frontend performance, introducing caches when economical
|
||||
to do so, and tightening existing systems up. For the linker, it has been mostly
|
||||
tuned thus far for giant projects, and so we'd like to improve linking speed for
|
||||
small-to-mid sized projects as well.
|
||||
|
||||
For the linker, there are also a number of features to come, like
|
||||
dead-code-elimination (`/opt:ref`), and link-time-optimizations with the help
|
||||
of `clang` (we won't support LTCG from MSVC, since it is undocumented).
|
||||
|
||||
### Local x64 Linux Debugging Phase
|
||||
|
||||
The next priority for the project is to take the rock solid x64 Windows
|
||||
debugging experience, and port all of the relevant pieces to support local x64
|
||||
Linux debugging also.
|
||||
|
||||
The debugger has been written to abstract over the parts that need to differ on
|
||||
either Linux or Windows, and this is mainly going to be a task in building out
|
||||
different backends for those abstraction layers.
|
||||
|
||||
The major parts of this phase are:
|
||||
|
||||
- Porting the `src/demon` layer to implement the Demon local process control
|
||||
abstraction API.
|
||||
- Implementing an x64 ELF Linux unwinder in the `src/ctrl` layer.
|
||||
- Creating a DWARF-to-RDI converter (in the same way that we've built a
|
||||
PDB-to-RDI converter). A partial implementation of this is in
|
||||
`src/rdi_from_dwarf`.
|
||||
- Porting the `src/render` layer to implement all of the rendering features the
|
||||
frontend needs on a Linux-compatible API (the backend used on Windows is D3D11).
|
||||
- Porting the `src/font_provider` layer to a Linux-compatible font
|
||||
rasterization backend, like FreeType (the backend used on Windows is
|
||||
DirectWrite).
|
||||
- Porting the `src/os` layers to Linux. This includes core operating system
|
||||
abstraction (virtual memory allocation, threading and synchronization
|
||||
primitives, and so on), and graphical operating system abstraction (windows,
|
||||
input events, and so on).
|
||||
|
||||
Once the above list is complete, and once every part is rock solid, the Windows
|
||||
debugging experience we'll have worked diligently to create will also be
|
||||
available natively on Linux machines.
|
||||
|
||||
### And Beyond!
|
||||
|
||||
There are several directions we might take after these two major phases,
|
||||
like remote debugging, porting to different architectures, further improving
|
||||
the debugger's features (like improving the visualization engine), and so on.
|
||||
But for now, we're mostly focused on those first two phases.
|
||||
|
||||
---
|
||||
|
||||
# Codebase Introduction
|
||||
|
||||
## Top-Level Directory Descriptions
|
||||
|
||||
- `data`: Small binary files which are used when building, either to embed
|
||||
within build artifacts, or to package with them.
|
||||
- `src`: All source code.
|
||||
|
||||
After setting up the codebase and building, the following directories will
|
||||
also exist:
|
||||
|
||||
- `build`: All build artifacts. Not checked in to version control.
|
||||
- `local`: Local files, used for local build configuration input files. Not
|
||||
checked in to version control.
|
||||
|
||||
## Layer Descriptions
|
||||
|
||||
The codebase is organized into *layers*. Layers are separated either to isolate
|
||||
certain problems, and to allow inclusion into various builds without needing to
|
||||
pull everything in the codebase into a build. Layers correspond with folders
|
||||
inside of the `src` directory. Sometimes, one folder inside of the `src`
|
||||
directory will include multiple sub-layers, but the structure is intended to be
|
||||
fairly flat.
|
||||
|
||||
Layers correspond roughly 1-to-1 with *namespaces*. The term "namespaces" in
|
||||
this context does not refer to specific namespace language features, but rather
|
||||
a naming convention for C-style namespaces, which are written in the codebase as
|
||||
a short prefix, usually 1-3 characters, followed by an underscore. These
|
||||
namespaces are used such that the layer to which certain code belongs may be
|
||||
quickly understood by glancing at code. The namespaces are generally quite short
|
||||
to ensure that they aren't much of a hassle to write. Sometimes, multiple sub-
|
||||
layers will share a namespace. A few layers do not have a namespace, but most
|
||||
do. Namespaces are either all-caps or lowercase depending on the context in
|
||||
which they're used. For types, enum values, and some macros, they are
|
||||
capitalized. For functions and global variables, they are lowercase.
|
||||
|
||||
Layers depend on other layers, but circular dependencies would break the
|
||||
separability and isolation utility of layers (in effect, forming one big layer),
|
||||
so in other words, layers are arranged into a directed acyclic graph.
|
||||
|
||||
A few layers are built to be used completely independently from the rest of the
|
||||
codebase, as libraries in other codebases and projects. As such, these layers do
|
||||
not depend on any other layers in the codebase. The folders which contain these
|
||||
layers are prefixed with `lib_`, like `lib_rdi`.
|
||||
|
||||
A list of the layers in the codebase and their associated namespaces is below:
|
||||
- `artifact_cache` (`AC_`): Implements an asynchronously-filled cache of
|
||||
computation artifacts, which are automatically evicted when not accessed. Used
|
||||
for asynchronously streaming and caching process memory and file system
|
||||
contents, as well as asynchronously preparing visualizer data.
|
||||
- `base` (no namespace): Universal, codebase-wide constructs. Strings, math,
|
||||
memory allocators, helper macros, command-line parsing, and so on. Requires
|
||||
no other codebase layers.
|
||||
- `codeview` (`CV_`): Code for parsing and writing the CodeView format.
|
||||
- `coff` (`COFF_`): Code for parsing and writing the COFF (Common Object File
|
||||
Format) file format.
|
||||
- `content` (`C_`): Implements a cache for general data blobs, keyed by a
|
||||
128-bit hash of the data. Also implements a keying system on top, where keys
|
||||
refer to a unique identity which corresponds to a history of 128-bit hashes.
|
||||
Used as a general data store by other layers.
|
||||
- `ctrl` (`CTRL_`): The debugger's "control system" layer. Implements
|
||||
asynchronous process control, stepping, and breakpoints for all attached
|
||||
processes. Runs in lockstep with attached processes. When it runs, attached
|
||||
processes are halted. When attached processes are running, it is halted.
|
||||
Driven by a debugger frontend on another thread.
|
||||
- `dbg_engine` (`D_`): Implements the core debugger system, without any
|
||||
graphical components. This contains top-level logic for things like stepping,
|
||||
launching, freezing threads, mid-run breakpoint addition, some caches, and so
|
||||
on.
|
||||
- `dbg_info` (`DI_`): Implements asynchronous debug info conversion and loading.
|
||||
Maintains a cache for loaded debug info. Loads RAD Debug Info (RDI) files.
|
||||
Launches separate processes for on-demand conversion to the RDI format if
|
||||
necessary. Also provides various asynchronous operations for using debug info,
|
||||
like fuzzy searching across all records in loaded debug info.
|
||||
- `demon` (`DMN_`): An abstraction layer for local-machine, low-level process
|
||||
control. The abstraction is used to provide a common interface for process
|
||||
control on target platforms. Used to implement part of `ctrl`.
|
||||
- `disasm` (`DASM_`): Implements disassembly generation, including exposing the
|
||||
ability to compute and cache disassembly asynchronously.
|
||||
- `draw` (`DR_`): Implements a high-level graphics drawing API for the
|
||||
debugger's purposes, using the underlying `render` abstraction layer. Provides
|
||||
high-level APIs for various draw commands, but takes care of batching them,
|
||||
and so on.
|
||||
- `dwarf` (`DW_`): Code for parsing the DWARF format.
|
||||
- `eh` (`EH_`): Code for parsing the EH frame format.
|
||||
- `elf` (`ELF_`): Code for parsing the ELF format.
|
||||
- `eval` (`E_`): A compiler for an expression language, built for evaluation of
|
||||
variables, registers, types, and more, from debugger-attached processes,
|
||||
debug info, debugger state, and files. Broken into several phases mostly
|
||||
corresponding to traditional compiler phases: lexer, parser, type-checker, IR
|
||||
generation, and IR evaluation.
|
||||
- `eval_visualization` (`EV_`): Implements the core non-graphical evaluation
|
||||
visualization engine, which can be used to visualize evaluations (provided by
|
||||
the `eval` layer) in a number of ways. Implements core data structures and
|
||||
transforms for watch tables.
|
||||
- `file_stream` (`FS_`): Implements asynchronous file streaming, storing the
|
||||
artifacts inside of the cache implemented by the `content` and
|
||||
`artifact_cache` layers, hot-reloading the contents of files when they change.
|
||||
Allows callers to map file paths to data hashes, which can then be used to
|
||||
obtain the file's data.
|
||||
- `font_cache` (`FNT_`): Implements a cache of rasterized font data, both in
|
||||
CPU-side data for text shaping, and in GPU texture atlases for rasterized
|
||||
glyphs. All cache information is sourced from the `font_provider` abstraction
|
||||
layer.
|
||||
- `font_provider` (`FP_`): An abstraction layer for various font file decoding
|
||||
and font rasterization backends.
|
||||
- `lib_raddbg_markup` (`RADDBG_`): Standalone library for marking up user
|
||||
programs to work with various features in the debugger. Does not depend on
|
||||
`base`, and can be independently relocated to other codebases.
|
||||
- `lib_rdi` (`RDI_`): Standalone library which defines the core RDI types
|
||||
and helper functions for reading and writing the RDI debug info file format.
|
||||
Does not depend on `base`, and can be independently relocated to other
|
||||
codebases.
|
||||
- `lib_rdi_make` (`RDIM_`): Standalone library for constructing RDI debug info
|
||||
data. Does not depend on `base`, and can be independently relocated
|
||||
to other codebases.
|
||||
- `linker` (`LNK_`): The layer which implements the RAD Linker executable
|
||||
itself.
|
||||
- `mdesk` (`MD_`): Code for parsing Metadesk files (stored as `.mdesk`), which
|
||||
is the JSON-like (technically a JSON superset) text format used for the
|
||||
debugger's user and project configuration files and metacode, which is parsed
|
||||
and used to generate code with the `metagen` layer.
|
||||
- `metagen` (`MG_`): A metaprogram which is used to generate primarily code and
|
||||
data tables. Consumes Metadesk files, stored with the extension `.mdesk`, and
|
||||
generates C code which is then included by hand-written C code. Currently, it
|
||||
does not analyze the codebase's hand-written C code, but in principle this is
|
||||
possible. This allows easier & less-error-prone management of large data
|
||||
tables, which are then used to produce e.g. C `enum`s and a number of
|
||||
associated data tables. There are also a number of other generation features,
|
||||
like embedding binary files or complex multi-line strings into source code.
|
||||
- `msf` (`MSF_`): Code for parsing and writing the MSF file format.
|
||||
- `msvc_crt` (`MSCRT_`): Code for parsing that's specific to the MSVC CRT.
|
||||
- `mule` (no namespace): Test executables for battle testing debugger
|
||||
functionality.
|
||||
- `mutable_text` (`MTX_`): Implements an asynchronously-filled-and-mutated
|
||||
cache for text buffers which are mutated across time. In the debugger, this is
|
||||
used to implement the `Output` log.
|
||||
- `natvis` (no namespace): NatVis files for type visualization of the codebase's
|
||||
types in other debuggers.
|
||||
- `os/core` (`OS_`): An abstraction layer providing core, non-graphical
|
||||
functionality from the operating system under an abstract API, which is
|
||||
implemented per-target-operating-system.
|
||||
- `os/gfx` (`OS_`): An abstraction layer, building on `os/core`, providing
|
||||
graphical operating system features under an abstract API, which is
|
||||
implemented per-target-operating-system.
|
||||
- `pdb` (`PDB_`): Code for parsing and writing the PDB file format.
|
||||
- `pe` (`PE_`): Code for parsing and writing the PE (Portable Executable) file
|
||||
format.
|
||||
- `radbin` (`RB_`): The layer implementing the `radbin` binary utility
|
||||
executable.
|
||||
- `raddbg` (`RD_`): The layer which ties everything together for the main
|
||||
graphical debugger executable. Implements the debugger's graphical frontend,
|
||||
all of the debugger-specific UI, the debugger executable's command line
|
||||
interface, and all of the built-in visualizers.
|
||||
- `rdi` (`RDI_`): A layer which includes the `lib_rdi` layer and bundles it with
|
||||
codebase-specific helpers, to easily include the library in codebase programs,
|
||||
and have it be integrated with codebase constructs.
|
||||
- `rdi_from_coff` (`C2R_`): Code for converting information in COFF files to the
|
||||
equivalent RDI data.
|
||||
- `rdi_from_dwarf` (`D2R_`): In-progress code for converting DWARF to the
|
||||
equivalent RDI data.
|
||||
- `rdi_from_elf` (`E2R_`)): Code for converting ELF data to the equivalent RDI
|
||||
data.
|
||||
- `rdi_from_pdb` (`P2R_`): Code for converting PDB data to the equivalent RDI
|
||||
data.
|
||||
- `rdi_make` (`RDIM_`): A layer which includes the `lib_rdi_make` layer and
|
||||
bundles it with codebase-specific helpers, to easily include the library in
|
||||
codebase programs, and have it be integrated with codebase constructs.
|
||||
- `regs` (`REGS_`): Types, helper functions, and metadata for registers on
|
||||
supported architectures. Used in reading/writing registers in `demon`, or in
|
||||
looking up register metadata.
|
||||
- `render` (`R_`): An abstraction layer providing an abstract API for rendering
|
||||
using various GPU APIs under a common interface. Does not implement a high
|
||||
level drawing API - this layer is strictly for minimally abstracting on an
|
||||
as-needed basis. Higher level drawing features are implemented in the `draw`
|
||||
layer.
|
||||
- `scratch` (no namespace): Scratch space for small and transient test programs.
|
||||
- `tester` (no namespace): A program used for automated testing.
|
||||
- `text` (`TXT_`): Implements text processing functions, like parsing line
|
||||
breaks, and lexing and parsing source code. Also offers an API to do this
|
||||
asynchronously.
|
||||
- `third_party` (no namespace): External code from other projects, which some
|
||||
layers in the codebase depend on. All external code is included and built
|
||||
directly within the codebase.
|
||||
- `ui` (`UI_`): Machinery for building graphical user interfaces. Provides a
|
||||
core immediate mode hierarchical user interface data structure building
|
||||
API, and has helper layers for building some higher-level widgets.
|
||||
# The RAD Debugger Project
|
||||
|
||||
_**Note:** This README does not document usage instructions and tips for the
|
||||
debugger itself, and is intended as a technical overview of the project. The
|
||||
debugger's README, which includes usage instructions and tips, can be found
|
||||
packaged along with debugger releases, or within the `build` folder after a
|
||||
local copy has been built._
|
||||
|
||||
The RAD Debugger is a native, user-mode, multi-process, graphical debugger. It
|
||||
currently only supports local-machine Windows x64 debugging with PDBs, with
|
||||
plans to expand and port in the future. In the future we'll expand to also
|
||||
support native Linux debugging and DWARF debug info.
|
||||
|
||||
The RAD Debugger is currently in *ALPHA*. In order to get the debugger bullet-
|
||||
proof, it'd greatly help out if you submitted the issues you find here, along
|
||||
with any information you can gather, like dump files (along with the build you
|
||||
used), instructions to reproduce, test executables, and so on.
|
||||
|
||||
You can download pre-built binaries for the debugger
|
||||
[here](https://github.com/EpicGames/raddebugger/releases).
|
||||
|
||||
The RAD Debugger project aims to simplify the debugger by simplifying and
|
||||
unifying the underlying debug info format. In that pursuit we've built the RAD
|
||||
Debug Info (RDI) format, which is what the debugger parses and uses. To work
|
||||
with existing toolchains, we convert PDB (and eventually PE/ELF files with
|
||||
embedded DWARF) into the RDI format on-demand.
|
||||
|
||||
The RDI format is currently specified in code, in the files within the
|
||||
`src/lib_rdi_format` folder. The other relevant folders for working with the
|
||||
format are:
|
||||
|
||||
- `lib_rdi_make`: The "RAD Debug Info Make" library, for making RDI debug info.
|
||||
- `rdi_from_pdb`: Our PDB-to-RDI converter. Can be used as a helper codebase
|
||||
layer, or built as an executable with a command line interface frontend.
|
||||
- `rdi_from_dwarf`: Our in-progress DWARF-to-RDI converter.
|
||||
- `rdi_dump`: Our RDI textual dumping utility.
|
||||
|
||||
## Development Setup Instructions
|
||||
|
||||
**Note: Currently, only x64 Windows development is supported.**
|
||||
|
||||
### 1. Installing the Required Tools (MSVC & Windows SDK)
|
||||
|
||||
In order to work with the codebase, you'll need the [Microsoft C/C++ Build Tools
|
||||
v15 (2017) or later](https://aka.ms/vs/17/release/vs_BuildTools.exe), for both
|
||||
the Windows SDK and the MSVC compiler and linker.
|
||||
|
||||
If the Windows SDK is installed (e.g. via installation of the Microsoft C/C++
|
||||
Build Tools), you may also build with [Clang](https://releases.llvm.org/).
|
||||
|
||||
### 2. Build Environment Setup
|
||||
|
||||
Building the codebase can be done in a terminal which is equipped with the
|
||||
ability to call either MSVC or Clang from command line.
|
||||
|
||||
This is generally done by calling `vcvarsall.bat x64`, which is included in the
|
||||
Microsoft C/C++ Build Tools. This script is automatically called by the `x64
|
||||
Native Tools Command Prompt for VS <year>` variant of the vanilla `cmd.exe`. If
|
||||
you've installed the build tools, this command prompt may be easily located by
|
||||
searching for `Native` from the Windows Start Menu search.
|
||||
|
||||
You can ensure that the MSVC compiler is accessible from your command line by
|
||||
running:
|
||||
|
||||
```
|
||||
cl
|
||||
```
|
||||
|
||||
If everything is set up correctly, you should have output very similar to the
|
||||
following:
|
||||
|
||||
```
|
||||
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30151 for x64
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
usage: cl [ option... ] filename... [ /link linkoption... ]
|
||||
```
|
||||
|
||||
### 3. Building
|
||||
|
||||
Within this terminal, `cd` to the root directory of the codebase, and just run
|
||||
the `build.bat` script:
|
||||
|
||||
```
|
||||
build
|
||||
```
|
||||
|
||||
You should see the following output:
|
||||
|
||||
```
|
||||
[debug mode]
|
||||
[msvc compile]
|
||||
[default mode, assuming `raddbg` build]
|
||||
metagen_main.c
|
||||
searching C:\devel\raddebugger/src... 299 files found
|
||||
parsing metadesk... 12 metadesk files parsed
|
||||
gathering tables... 37 tables found
|
||||
generating layer code...
|
||||
raddbg.cpp
|
||||
```
|
||||
|
||||
If everything worked correctly, there will be a `build` folder in the root
|
||||
level of the codebase, and it will contain a freshly-built `raddbg.exe`.
|
||||
|
||||
## Short-To-Medium-Term Roadmap
|
||||
|
||||
### The Initial Alpha Battle-Testing Phase
|
||||
|
||||
The first priority for the project is to ensure that the most crucial debugger
|
||||
components are functioning extremely reliably for local, x64, Windows
|
||||
debugging. This would include parts like debug info conversion, debug info
|
||||
loading, process control, stepping, evaluation (correct usage of both location
|
||||
info and type info), and a robust frontend which ensures the lower level parts
|
||||
are usable.
|
||||
|
||||
We feel that the debugger has already come a long way in all of these respects,
|
||||
but given the massive set of possible combinations of languages, build
|
||||
settings, toolchains, used language features, and patterns of generated code,
|
||||
there are still cases where the debugger has not been tested, and so there are
|
||||
still issues. So, we feel that the top priority is eliminating these issues,
|
||||
such that the debugging experience is rock solid.
|
||||
|
||||
### Local x64 Linux Debugging Phase
|
||||
|
||||
The next priority for the project is to take the rock solid x64 Windows
|
||||
debugging experience, and port all of the relevant pieces to support local x64
|
||||
Linux debugging also.
|
||||
|
||||
The debugger has been written to abstract over the parts that need to differ on
|
||||
either Linux or Windows, and this is mainly going to be a task in building out
|
||||
different backends for those abstraction layers.
|
||||
|
||||
The major parts of this phase are:
|
||||
|
||||
- Porting the `src/demon` layer to implement the Demon local process control
|
||||
abstraction API.
|
||||
- Implementing an x64 ELF Linux unwinder in the `src/ctrl` layer.
|
||||
- Creating a DWARF-to-RDI converter (in the same way that we've built a
|
||||
PDB-to-RDI converter). A partial implementation of this is in
|
||||
`src/rdi_from_dwarf`.
|
||||
- Porting the `src/render` layer to implement all of the rendering features the
|
||||
frontend needs on a Linux-compatible API (the backend used on Windows is D3D11).
|
||||
- Porting the `src/font_provider` layer to a Linux-compatible font
|
||||
rasterization backend, like FreeType (the backend used on Windows is
|
||||
DirectWrite).
|
||||
- Porting the `src/os` layers to Linux. This includes core operating system
|
||||
abstraction (virtual memory allocation, threading and synchronization
|
||||
primitives, and so on), and graphical operating system abstraction (windows,
|
||||
input events, and so on).
|
||||
|
||||
Once the above list is complete, and once every part is rock solid, the Windows
|
||||
debugging experience we'll have worked diligently to create will also be
|
||||
available natively on Linux machines.
|
||||
|
||||
### And Beyond!
|
||||
|
||||
There are several directions we might take after these two major phases,
|
||||
like remote debugging, porting to different architectures, further improving
|
||||
the debugger's features (like improving the visualization engine), and so on.
|
||||
But for now, we're mostly focused on those first two phases.
|
||||
|
||||
## Top-Level Directory Descriptions
|
||||
|
||||
- `data`: Small binary files which are used when building, either to embed
|
||||
within build artifacts, or to package with them.
|
||||
- `src`: All source code.
|
||||
|
||||
After setting up the codebase and building, the following directories will
|
||||
also exist:
|
||||
|
||||
- `build`: All build artifacts. Not checked in to version control.
|
||||
- `local`: Local files, used for local build configuration input files.
|
||||
|
||||
## Codebase Introduction
|
||||
|
||||
The codebase is organized into *layers*. Layers are separated either to isolate
|
||||
certain problems, and to allow inclusion into various builds without needing to
|
||||
pull everything in the codebase into a build. Layers correspond with folders
|
||||
inside of the `src` directory. Sometimes, one folder inside of the `src`
|
||||
directory will include multiple sub-layers, but the structure is intended to be
|
||||
fairly flat.
|
||||
|
||||
Layers correspond roughly 1-to-1 with *namespaces*. The term "namespaces" in
|
||||
this context does not refer to specific namespace language features, but rather
|
||||
a naming convention for C-style namespaces, which are written in the codebase as
|
||||
a short prefix, usually 1-3 characters, followed by an underscore. These
|
||||
namespaces are used such that the layer to which certain code belongs may be
|
||||
quickly understood by glancing at code. The namespaces are generally quite short
|
||||
to ensure that they aren't much of a hassle to write. Sometimes, multiple sub-
|
||||
layers will share a namespace. A few layers do not have a namespace, but most
|
||||
do. Namespaces are either all-caps or lowercase depending on the context in
|
||||
which they're used. For types, enum values, and some macros, they are
|
||||
capitalized. For functions and global variables, they are lowercase.
|
||||
|
||||
Layers depend on other layers, but circular dependencies would break the
|
||||
separability and isolation utility of layers (in effect, forming one big layer),
|
||||
so in other words, layers are arranged into a directed acyclic graph.
|
||||
|
||||
A few layers are built to be used completely independently from the rest of the
|
||||
codebase, as libraries in other codebases and projects. As such, these layers do
|
||||
not depend on any other layers in the codebase. The folders which contain these
|
||||
layers are prefixed with `lib_`, like `lib_rdi_format`.
|
||||
|
||||
A list of the layers in the codebase and their associated namespaces is below:
|
||||
- `base` (no namespace): Universal, codebase-wide constructs. Strings, math,
|
||||
memory allocators, helper macros, command-line parsing, and so on. Depends
|
||||
on no other codebase layers.
|
||||
- `codeview` (`CV_`): Code for parsing and/or writing the CodeView format.
|
||||
- `coff` (`COFF_`): Code for parsing and/or writing the COFF (Common Object File
|
||||
Format) file format.
|
||||
- `ctrl` (`CTRL_`): The debugger's "control system" layer. Implements
|
||||
asynchronous process control, stepping, and breakpoints for all attached
|
||||
processes. Runs in lockstep with attached processes. When it runs, attached
|
||||
processes are halted. When attached processes are running, it is halted.
|
||||
Driven by a debugger frontend on another thread.
|
||||
- `dasm` (`DASM_`): An asynchronous disassembly decoder and cache. Users ask for
|
||||
disassembly for a particular virtual address range in a process, and threads
|
||||
implemented in this layer decode and cache the disassembly for that range.
|
||||
- `dbgi` (`DI_`): An asynchronous debug info loader and cache. Loads debug info
|
||||
stored in the RDI format. Users ask for debug info for a particular path, and
|
||||
on separate threads, this layer loads the associated debug info file. If
|
||||
necessary, it will launch a separate conversion process to convert original
|
||||
debug info into the RDI format.
|
||||
- `demon` (`DEMON_`): An abstraction layer for local-machine, low-level process
|
||||
control. The abstraction is used to provide a common interface for process
|
||||
control on target platforms. Used to implement part of `ctrl`.
|
||||
- `df/core` (`DF_`): The debugger's non-graphical frontend. Implements a
|
||||
debugger "entity cache" (where "entities" include processes, threads, modules,
|
||||
breakpoints, source files, targets, and so on). Implements a command loop
|
||||
for driving process control, which is used to implement stepping commands and
|
||||
user breakpoints. Implements extractors and caches for various entity-related
|
||||
data, like full thread unwinds and local variable maps. Also implements core
|
||||
building blocks for evaluation and evaluation visualization.
|
||||
- `df/gfx` (`DF_`): The debugger's graphical frontend. Builds on top of
|
||||
`df/core` to provide all graphical features, including windows, panels, all
|
||||
of the various debugger interfaces, and evaluation visualization.
|
||||
- `draw` (`D_`): Implements a high-level graphics drawing API for the debugger's
|
||||
purposes, using the underlying `render` abstraction layer. Provides high-level
|
||||
APIs for various draw commands, but takes care of batching them, and so on.
|
||||
- `eval` (`EVAL_`): Implements a compiler for an expression language built for
|
||||
evaluation of variables, registers, and so on from debugger-attached processes
|
||||
and/or debug info. Broken into several phases mostly corresponding to
|
||||
traditional compiler phases - lexer, parser, type-checker, IR generation, and
|
||||
IR evaluation.
|
||||
- `font_cache` (`F_`): Implements a cache of rasterized font data, both in CPU-
|
||||
side data for text shaping, and in GPU texture atlases for rasterized glyphs.
|
||||
All cache information is sourced from the `font_provider` abstraction layer.
|
||||
- `font_provider` (`FP_`): An abstraction layer for various font file decoding
|
||||
and font rasterization backends.
|
||||
- `geo_cache` (`GEO_`): Implements an asynchronously-filled cache for GPU
|
||||
geometry data, filled by data sourced in the `hash_store` layer's cache. Used
|
||||
for asynchronously preparing data for memory visualization in the debugger.
|
||||
- `hash_store` (`HS_`): Implements a cache for general data blobs, keyed by a
|
||||
128-bit hash of the data. Used as a general data store by other layers.
|
||||
- `lib_raddbg_markup` (`RADDBG_`): Standalone library for marking up user
|
||||
programs to work with various features in the `raddbg` debugger. Does not
|
||||
depend on `base`, and can be independently relocated to other codebases.
|
||||
- `lib_rdi_make` (`RDIM_`): Standalone library for constructing RDI debug info
|
||||
data. Does not depend on `base`, and can be independently relocated
|
||||
to other codebases.
|
||||
- `lib_rdi_format` (`RDI_`): Standalone library which defines the core RDI types
|
||||
and helper functions for reading and writing the RDI debug info file format.
|
||||
Does not depend on `base`, and can be independently relocated to other
|
||||
codebases.
|
||||
- `metagen` (`MG_`): A metaprogram which is used to generate primarily code and
|
||||
data tables. Consumes Metadesk files, stored with the extension `.mdesk`, and
|
||||
generates C code which is then included by hand-written C code. Currently, it
|
||||
does not analyze the codebase's hand-written C code, but in principle this is
|
||||
possible. This allows easier & less-error-prone management of large data
|
||||
tables, which are then used to produce e.g. C `enum`s and a number of
|
||||
associated data tables. There are also a number of other generation features,
|
||||
like embedding binary files or complex multi-line strings into source code.
|
||||
This layer cannot depend on any other layer in the codebase directly,
|
||||
including `base`, because it may be used to generate code for those layers. To
|
||||
still use `base` and `os` layer features in the `metagen` program, a separate,
|
||||
duplicate version of `base` and `os` are included in this layer. They are
|
||||
updated manually, as needed. This is to ensure the stability of the
|
||||
metaprogram.
|
||||
- `msf` (`MSF_`): Code for parsing and/or writing the MSF file format.
|
||||
- `mule` (no namespace): Test executables for battle testing debugger
|
||||
functionality.
|
||||
- `natvis` (no namespace): NatVis files for type visualization of the codebase's
|
||||
types in other debuggers.
|
||||
- `os/core` (`OS_`): An abstraction layer providing core, non-graphical
|
||||
functionality from the operating system under an abstract API, which is
|
||||
implemented per-target-operating-system.
|
||||
- `os/gfx` (`OS_`): An abstraction layer, building on `os/core`, providing
|
||||
graphical operating system features under an abstract API, which is
|
||||
implemented per-target-operating-system.
|
||||
- `os/socket` (`OS_`): An abstraction layer, building on `os/core`, providing
|
||||
networking operating system features under an abstract API, which is
|
||||
implemented per-target-operating-system.
|
||||
- `pdb` (`PDB_`): Code for parsing and/or writing the PDB file format.
|
||||
- `pe` (`PE_`): Code for parsing and/or writing the PE (Portable Executable)
|
||||
file format.
|
||||
- `raddbg` (no namespace): The layer which ties everything together for the main
|
||||
graphical debugger. Not much "meat", just drives `df`, implements command line
|
||||
options, and so on.
|
||||
- `rdi_from_pdb` (`P2R_`): Our implementation of PDB-to-RDI conversion.
|
||||
- `rdi_from_dwarf` (`D2R_`): Our in-progress implementation of DWARF-to-RDI
|
||||
conversion.
|
||||
- `rdi_dump` (no namespace): A dumper utility program for dumping
|
||||
textualizations of RDI debug info files.
|
||||
- `regs` (`REGS_`): Types, helper functions, and metadata for registers on
|
||||
supported architectures. Used in reading/writing registers in `demon`, or in
|
||||
looking up register metadata.
|
||||
- `render` (`R_`): An abstraction layer providing an abstract API for rendering
|
||||
using various GPU APIs under a common interface. Does not implement a high
|
||||
level drawing API - this layer is strictly for minimally abstracting on an
|
||||
as-needed basis. Higher level drawing features are implemented in the `draw`
|
||||
layer.
|
||||
- `scratch` (no namespace): Scratch space for small and transient test or sample
|
||||
programs.
|
||||
- `texture_cache` (`TEX_`): Implements an asynchronously-filled cache for GPU
|
||||
texture data, filled by data sourced in the `hash_store` layer's cache. Used
|
||||
for asynchronously preparing data for memory visualization in the debugger.
|
||||
- `txti` (`TXTI_`): Machinery for asynchronously-loaded, asynchronously hot-
|
||||
reloaded, asynchronously parsed, and asynchronously mutated source code files.
|
||||
Used by the debugger to visualize source code files. Users ask for text lines,
|
||||
tokens, and metadata, and it is prepared on background threads.
|
||||
- `type_graph` (`TG_`): Code for analyzing and navigating type structures from
|
||||
RDI debug info files, with the additional capability of constructing
|
||||
synthetic types *not* found in debug info. Used in `eval` and for various
|
||||
visualization features.
|
||||
- `ui` (`UI_`): Machinery for building graphical user interfaces. Provides a
|
||||
core immediate mode hierarchical user interface data structure building
|
||||
API, and has helper layers for building some higher-level widgets.
|
||||
|
||||
@@ -1,189 +1,128 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
cd /D "%~dp0"
|
||||
:restart
|
||||
|
||||
:: --- Usage Notes (2024/1/10) ------------------------------------------------
|
||||
::
|
||||
:: This is a central build script for the RAD Debugger project, for use in
|
||||
:: Windows development environments. It takes a list of simple alphanumeric-
|
||||
:: only arguments which control (a) what is built, (b) which compiler & linker
|
||||
:: are used, and (c) extra high-level build options. By default, if no options
|
||||
:: are passed, then the main "raddbg" graphical debugger is built.
|
||||
::
|
||||
:: Below is a non-exhaustive list of possible ways to use the script:
|
||||
:: `build raddbg`
|
||||
:: `build raddbg clang`
|
||||
:: `build raddbg release`
|
||||
:: `build raddbg asan telemetry`
|
||||
:: `build rdi_from_pdb`
|
||||
::
|
||||
:: For a full list of possible build targets and their build command lines,
|
||||
:: search for @build_targets in this file.
|
||||
::
|
||||
:: Below is a list of all possible non-target command line options:
|
||||
::
|
||||
:: - `asan`: enable address sanitizer
|
||||
:: - `ubsan`: enable undefined-behavior sanitizer
|
||||
:: - `telemetry`: enable RAD telemetry profiling support
|
||||
:: - `spall`: enable spall profiling support
|
||||
|
||||
:: --- Unpack Arguments -------------------------------------------------------
|
||||
for %%a in (%*) do set "%%~a=1"
|
||||
if not "%msvc%"=="1" if not "%clang%"=="1" set msvc=1
|
||||
if not "%release%"=="1" set debug=1
|
||||
if "%debug%"=="1" set release=0 && echo [debug mode]
|
||||
if "%release%"=="1" set debug=0 && echo [release mode]
|
||||
if "%msvc%"=="1" set clang=0 && echo [msvc compile]
|
||||
if "%clang%"=="1" set msvc=0 && echo [clang compile]
|
||||
if "%~1"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1&& set com_shim=1&& set raddbg_com_shim=1
|
||||
if "%~1"=="release" if "%~2"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1&& set com_shim=1&& set raddbg_com_shim=1
|
||||
|
||||
:: --- Unpack Command Line Build Arguments ------------------------------------
|
||||
set auto_compile_flags=
|
||||
if "%telemetry%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_TELEMETRY=1 && echo [telemetry profiling enabled]
|
||||
if "%spall%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_SPALL=1 && echo [spall profiling enabled]
|
||||
if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=address && echo [asan enabled]
|
||||
if "%ubsan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=undefined && echo [ubsan enabled]
|
||||
if "%opengl%"=="1" set auto_compile_flags=%auto_compile_flags% -DR_BACKEND=R_BACKEND_OPENGL && echo [opengl render backend]
|
||||
if "%dwarf%"=="1" if "%clang%"=="1" set auto_compile_flags=%auto_compile_flags% -gdwarf && echo [dwarf debug info]
|
||||
if "%dwarf%"=="" if "%clang%"=="1" set auto_compile_flags=%auto_compile_flags% -gcodeview
|
||||
if "%pgo%"=="1" (
|
||||
where llvm-profdata /q || echo llvm-profdata is not in the PATH || exit /b 1
|
||||
if "%clang%"=="1" (
|
||||
if "%pgo_run%" == "1" (
|
||||
call llvm-profdata merge %LLVM_PROFILE_FILE% -output=%~dp0build\build.profdata || exit /b 1
|
||||
set auto_compile_flags=%auto_compile_flags% -fprofile-use=%~dp0build\build.profdata
|
||||
set pgo_run=0
|
||||
) else (
|
||||
echo [pgo enabled]
|
||||
set auto_compile_flags=%auto_compile_flags% -fprofile-generate -mllvm -vp-counters-per-site=5
|
||||
set LLVM_PROFILE_FILE=%~dp0build\build.profraw
|
||||
set pgo_run=1
|
||||
)
|
||||
) else (
|
||||
echo ERROR: PGO build is not supported with current compiler
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
:: --- Compile/Link Line Definitions ------------------------------------------
|
||||
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7 /Zc:preprocessor
|
||||
set cl_debug= call cl /Od /Ob1 /DBUILD_DEBUG=1 %cl_common% %auto_compile_flags%
|
||||
set cl_release= call cl /O2 /DBUILD_DEBUG=0 %cl_common% %auto_compile_flags%
|
||||
set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /pdbaltpath:%%%%_PDB%%%% /NATVIS:"%~dp0\src\natvis\base.natvis" /noexp /nocoffgrpinfo /opt:ref /opt:icf
|
||||
set cl_out= /out:
|
||||
set cl_obj_out= /Fo:
|
||||
set cl_linker=
|
||||
set clang_common= -mcx16 -I..\src\ -I..\local\ -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-unused-parameter -Wno-writable-strings -Wno-missing-field-initializers -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf -ferror-limit=10000
|
||||
set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 -D_DEBUG %clang_common% %auto_compile_flags%
|
||||
set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 -DNDEBUG %clang_common% %auto_compile_flags%
|
||||
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /pdbaltpath:%%%%_PDB%%%% -Xlinker /NATVIS:"%~dp0\src\natvis\base.natvis" -Xlinker /opt:ref -Xlinker /opt:noicf
|
||||
set clang_out= -o
|
||||
set clang_obj_out= -o
|
||||
set clang_linker= -Xlinker
|
||||
|
||||
:: --- Per-Build Settings -----------------------------------------------------
|
||||
set link_icon=logo.res
|
||||
if "%msvc%"=="1" set linker=%cl_linker%
|
||||
if "%clang%"=="1" set linker=%clang_linker%
|
||||
if "%msvc%"=="1" set only_compile=/c
|
||||
if "%clang%"=="1" set only_compile=-c
|
||||
if "%msvc%"=="1" set EHsc=/EHsc
|
||||
if "%clang%"=="1" set EHsc=
|
||||
if "%msvc%"=="1" set no_aslr=/DYNAMICBASE:NO
|
||||
if "%clang%"=="1" set no_aslr=-Wl,/DYNAMICBASE:NO
|
||||
if "%msvc%"=="1" set rc=call rc
|
||||
if "%clang%"=="1" set rc=call llvm-rc
|
||||
if "%msvc%"=="1" set link_dll=/link /DLL
|
||||
if "%clang%"=="1" set link_dll=-Xlinker -DLL
|
||||
|
||||
:: --- Choose Compile/Link Lines ----------------------------------------------
|
||||
if "%msvc%"=="1" set compile_debug=%cl_debug%
|
||||
if "%msvc%"=="1" set compile_release=%cl_release%
|
||||
if "%msvc%"=="1" set compile_link=%cl_link%
|
||||
if "%msvc%"=="1" set out=%cl_out%
|
||||
if "%msvc%"=="1" set obj_out=%cl_obj_out%
|
||||
if "%clang%"=="1" set compile_debug=%clang_debug%
|
||||
if "%clang%"=="1" set compile_release=%clang_release%
|
||||
if "%clang%"=="1" set compile_link=%clang_link%
|
||||
if "%clang%"=="1" set out=%clang_out%
|
||||
if "%clang%"=="1" set obj_out=%clang_obj_out%
|
||||
if "%debug%"=="1" set compile=%compile_debug%
|
||||
if "%release%"=="1" set compile=%compile_release%
|
||||
|
||||
:: --- Prep Directories -------------------------------------------------------
|
||||
if not exist build mkdir build
|
||||
if not exist local mkdir local
|
||||
|
||||
:: --- Produce Logo Icon File -------------------------------------------------
|
||||
pushd build
|
||||
%rc% /nologo /fo logo.res ..\data\logo.rc || exit /b 1
|
||||
popd
|
||||
|
||||
:: --- Get Current Git Commit Id ----------------------------------------------
|
||||
for /f %%i in ('call git describe --always --dirty') do set compile=%compile% -DBUILD_GIT_HASH=\"%%i\"
|
||||
for /f %%i in ('call git rev-parse HEAD') do set compile=%compile% -DBUILD_GIT_HASH_FULL=\"%%i\"
|
||||
|
||||
:: --- Build & Run Metaprogram ------------------------------------------------
|
||||
pushd build
|
||||
if "%meta%"=="1" (
|
||||
echo [building metagen]
|
||||
%compile_debug% ..\src\metagen\metagen_main.c %compile_link% %out%metagen.exe || exit /b 1
|
||||
)
|
||||
if "%no_meta%"=="" if exist metagen.exe (
|
||||
echo [running metagen]
|
||||
metagen.exe || exit /b 1
|
||||
)
|
||||
popd
|
||||
|
||||
:: --- Build Everything (@build_targets) --------------------------------------
|
||||
pushd build
|
||||
if "%raddbg%"=="1" set didbuild=1 && %compile% ..\src\raddbg\raddbg_main.c %compile_link% %link_icon% %out%raddbg.exe || exit /b 1
|
||||
if "%raddbg_non_graphical%"=="1" set didbuild=1 && %compile% -DWM_STUB=1 -DR_BACKEND=R_BACKEND_STUB ..\src\raddbg\raddbg_main.c %compile_link% %link_icon% %out%raddbg_non_graphical.exe || exit /b 1
|
||||
if "%com_shim%"=="1" set didbuild=1 && %compile% ..\src\com_shim\com_shim_main.c %compile_link% %out%com_shim.exe || exit /b 1
|
||||
if "%radlink%"=="1" set didbuild=1 && %compile% ..\src\linker\lnk.c %compile_link% %linker% /NOIMPLIB %linker% /NATVIS:"%~dp0\src\linker\linker.natvis" %out%radlink.exe || exit /b 1
|
||||
if "%radbin%"=="1" set didbuild=1 && %compile% ..\src\radbin\radbin_main.c %compile_link% %out%radbin.exe || exit /b 1
|
||||
if "%raddump%"=="1" set didbuild=1 && %compile% ..\src\raddump\raddump_main.c %compile_link% %out%raddump.exe || exit /b 1
|
||||
if "%ryan_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1
|
||||
if "%textperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\textperf.c %compile_link% %out%textperf.exe || exit /b 1
|
||||
if "%convertperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\convertperf.c %compile_link% %out%convertperf.exe || exit /b 1
|
||||
if "%debugstringperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\debugstringperf.c %compile_link% %out%debugstringperf.exe || exit /b 1
|
||||
if "%parse_inline_sites%"=="1" set didbuild=1 && %compile% ..\src\scratch\parse_inline_sites.c %compile_link% %out%parse_inline_sites.exe || exit /b 1
|
||||
if "%strip_lib_debug%"=="1" set didbuild=1 && %compile% ..\src\strip_lib_debug\strip_lib_debug.c %compile_link% %out%strip_lib_debug.exe || exit /b 1
|
||||
if "%mule_main%"=="1" set didbuild=1 && del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp %obj_out%mule_inline.obj && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp %obj_out%mule_o2.obj && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1
|
||||
if "%mule_module%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_module.cpp %link_dll% %out%mule_module.dll || exit /b 1
|
||||
if "%mule_hotload%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_hotload_main.c %compile_link% %out%mule_hotload.exe & %compile% ..\src\mule\mule_hotload_module_main.c %compile_link% %link_dll% %out%mule_hotload_module.dll || exit /b 1
|
||||
if "%torture%"=="1" set didbuild=1 && %compile% ..\src\torture\torture_main.c %compile_link% %out%torture.exe || exit /b1
|
||||
if "%dwarf_expr_test%"=="1" set didbuild=1 && %compile% ..\src\torture\dwarf_expr_test.c %compile_link% %out%dwarf_expr_test.exe || exit /b1
|
||||
if "%unit_sizes_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\scratch\unit_sizes_from_pdb.c %compile_link% %out%unit_sizes_from_pdb.exe || exit /b1
|
||||
if "%mule_peb_trample%"=="1" (
|
||||
set didbuild=1
|
||||
if exist mule_peb_trample.exe move mule_peb_trample.exe mule_peb_trample_old_%random%.exe
|
||||
if exist mule_peb_trample_new.pdb move mule_peb_trample_new.pdb mule_peb_trample_old_%random%.pdb
|
||||
if exist mule_peb_trample_new.rdi move mule_peb_trample_new.rdi mule_peb_trample_old_%random%.rdi
|
||||
%compile% ..\src\mule\mule_peb_trample.c %compile_link% %out%mule_peb_trample_new.exe || exit /b 1
|
||||
move mule_peb_trample_new.exe mule_peb_trample.exe
|
||||
)
|
||||
popd
|
||||
|
||||
:: --- Set Up Debugger Com Shim -----------------------------------------------
|
||||
pushd build
|
||||
if "%raddbg_com_shim%"=="1" copy /y com_shim.exe raddbg.com
|
||||
popd
|
||||
|
||||
:: --- Warn On No Builds ------------------------------------------------------
|
||||
if "%didbuild%"=="" (
|
||||
echo [WARNING] no valid build target specified; must use build target names as arguments to this script, like `build raddbg` or `build rdi_from_pdb`.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: --- PGO Run ----------------------------------------------------------------
|
||||
if "%pgo_run%"=="1" (
|
||||
if "%radlink%"=="1" (
|
||||
pushd local\lyra_pgo
|
||||
call %~dp0build\radlink @lyra.rsp || exit /b 1
|
||||
popd
|
||||
)
|
||||
goto restart
|
||||
)
|
||||
@echo off
|
||||
setlocal
|
||||
cd /D "%~dp0"
|
||||
|
||||
:: --- Usage Notes (2024/1/10) ------------------------------------------------
|
||||
::
|
||||
:: This is a central build script for the RAD Debugger project. It takes a list
|
||||
:: of simple alphanumeric-only arguments which control (a) what is built, (b)
|
||||
:: which compiler & linker are used, and (c) extra high-level build options. By
|
||||
:: default, if no options are passed, then the main "raddbg" graphical debugger
|
||||
:: is built.
|
||||
::
|
||||
:: Below is a non-exhaustive list of possible ways to use the script:
|
||||
:: `build raddbg`
|
||||
:: `build raddbg clang`
|
||||
:: `build raddbg release`
|
||||
:: `build raddbg asan telemetry`
|
||||
:: `build rdi_from_pdb`
|
||||
::
|
||||
:: For a full list of possible build targets and their build command lines,
|
||||
:: search for @build_targets in this file.
|
||||
::
|
||||
:: Below is a list of all possible non-target command line options:
|
||||
::
|
||||
:: - `asan`: enable address sanitizer
|
||||
:: - `telemetry`: enable RAD telemetry profiling support
|
||||
|
||||
:: --- Unpack Arguments -------------------------------------------------------
|
||||
for %%a in (%*) do set "%%a=1"
|
||||
if not "%msvc%"=="1" if not "%clang%"=="1" set msvc=1
|
||||
if not "%release%"=="1" set debug=1
|
||||
if "%debug%"=="1" set release=0 && echo [debug mode]
|
||||
if "%release%"=="1" set debug=0 && echo [release mode]
|
||||
if "%msvc%"=="1" set clang=0 && echo [msvc compile]
|
||||
if "%clang%"=="1" set msvc=0 && echo [clang compile]
|
||||
if "%~1"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1
|
||||
|
||||
:: --- Unpack Command Line Build Arguments ------------------------------------
|
||||
set auto_compile_flags=
|
||||
if "%telemetry%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_TELEMETRY=1 && echo [telemetry profiling enabled]
|
||||
if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=address && echo [asan enabled]
|
||||
|
||||
:: --- Compile/Link Line Definitions ------------------------------------------
|
||||
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7
|
||||
set clang_common= -I..\src\ -I..\local\ -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-writable-strings -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf
|
||||
set cl_debug= call cl /Od /DBUILD_DEBUG=1 %cl_common% %auto_compile_flags%
|
||||
set cl_release= call cl /O2 /DBUILD_DEBUG=0 %cl_common% %auto_compile_flags%
|
||||
set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 %clang_common% %auto_compile_flags%
|
||||
set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 %clang_common% %auto_compile_flags%
|
||||
set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /natvis:"%~dp0\src\natvis\base.natvis" logo.res
|
||||
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /natvis:"%~dp0\src\natvis\base.natvis" logo.res
|
||||
set cl_out= /out:
|
||||
set clang_out= -o
|
||||
|
||||
:: --- Per-Build Settings -----------------------------------------------------
|
||||
set link_dll=-DLL
|
||||
if "%msvc%"=="1" set only_compile=/c
|
||||
if "%clang%"=="1" set only_compile=-c
|
||||
if "%msvc%"=="1" set EHsc=/EHsc
|
||||
if "%clang%"=="1" set EHsc=
|
||||
if "%msvc%"=="1" set no_aslr=/DYNAMICBASE:NO
|
||||
if "%clang%"=="1" set no_aslr=
|
||||
if "%msvc%"=="1" set rc=rc.exe
|
||||
if "%clang%"=="1" set rc=llvm-rc.exe
|
||||
|
||||
:: --- Choose Compile/Link Lines ----------------------------------------------
|
||||
if "%msvc%"=="1" set compile_debug=%cl_debug%
|
||||
if "%msvc%"=="1" set compile_release=%cl_release%
|
||||
if "%msvc%"=="1" set compile_link=%cl_link%
|
||||
if "%msvc%"=="1" set out=%cl_out%
|
||||
if "%clang%"=="1" set compile_debug=%clang_debug%
|
||||
if "%clang%"=="1" set compile_release=%clang_release%
|
||||
if "%clang%"=="1" set compile_link=%clang_link%
|
||||
if "%clang%"=="1" set out=%clang_out%
|
||||
if "%debug%"=="1" set compile=%compile_debug%
|
||||
if "%release%"=="1" set compile=%compile_release%
|
||||
|
||||
:: --- Prep Directories -------------------------------------------------------
|
||||
if not exist build mkdir build
|
||||
if not exist local mkdir local
|
||||
|
||||
:: --- Produce Logo Icon File -------------------------------------------------
|
||||
pushd build
|
||||
%rc% /nologo /fo logo.res ..\data\logo.rc || exit /b 1
|
||||
popd
|
||||
|
||||
:: --- Get Current Git Commit Id ----------------------------------------------
|
||||
for /f %%i in ('call git describe --always --dirty') do set compile=%compile% -DBUILD_GIT_HASH=\"%%i\"
|
||||
|
||||
:: --- Build & Run Metaprogram ------------------------------------------------
|
||||
if "%no_meta%"=="1" echo [skipping metagen]
|
||||
if not "%no_meta%"=="1" (
|
||||
pushd build
|
||||
%compile_debug% ..\src\metagen\metagen_main.c %compile_link% %out%metagen.exe || exit /b 1
|
||||
metagen.exe || exit /b 1
|
||||
popd
|
||||
)
|
||||
|
||||
:: --- Build Everything (@build_targets) --------------------------------------
|
||||
pushd build
|
||||
if "%raddbg%"=="1" %compile% %gfx% ..\src\raddbg\raddbg_main.cpp %compile_link% %out%raddbg.exe || exit /b 1
|
||||
if "%rdi_from_pdb%"=="1" %compile% ..\src\rdi_from_pdb\rdi_from_pdb_main.c %compile_link% %out%rdi_from_pdb.exe || exit /b 1
|
||||
if "%rdi_from_dwarf%"=="1" %compile% ..\src\rdi_from_dwarf\rdi_from_dwarf.c %compile_link% %out%rdi_from_dwarf.exe || exit /b 1
|
||||
if "%rdi_dump%"=="1" %compile% ..\src\rdi_dump\rdi_dump_main.c %compile_link% %out%rdi_dump.exe || exit /b 1
|
||||
if "%rdi_breakpad_from_pdb%"=="1" %compile% ..\src\rdi_breakpad_from_pdb\rdi_breakpad_from_pdb_main.c %compile_link% %out%rdi_breakpad_from_pdb.exe || exit /b 1
|
||||
if "%ryan_scratch%"=="1" %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1
|
||||
if "%cpp_tests%"=="1" %compile% ..\src\scratch\i_hate_c_plus_plus.cpp %compile_link% %out%cpp_tests.exe || exit /b 1
|
||||
if "%look_at_raddbg%"=="1" %compile% ..\src\scratch\look_at_raddbg.c %compile_link% %out%look_at_raddbg.exe || exit /b 1
|
||||
if "%mule_main%"=="1" del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1
|
||||
if "%mule_module%"=="1" %compile% ..\src\mule\mule_module.cpp %compile_link% %link_dll% %out%mule_module.dll || exit /b 1
|
||||
if "%mule_hotload%"=="1" %compile% ..\src\mule\mule_hotload_main.c %compile_link% %out%mule_hotload.exe & %compile% ..\src\mule\mule_hotload_module_main.c %compile_link% %link_dll% %out%mule_hotload_module.dll || exit /b 1
|
||||
if "%mule_peb_trample%"=="1" (
|
||||
if exist mule_peb_trample.exe move mule_peb_trample.exe mule_peb_trample_old_%random%.exe
|
||||
if exist mule_peb_trample_new.pdb move mule_peb_trample_new.pdb mule_peb_trample_old_%random%.pdb
|
||||
if exist mule_peb_trample_new.rdi move mule_peb_trample_new.rdi mule_peb_trample_old_%random%.rdi
|
||||
%compile% ..\src\mule\mule_peb_trample.c %compile_link% %out%mule_peb_trample_new.exe || exit /b 1
|
||||
move mule_peb_trample_new.exe mule_peb_trample.exe
|
||||
)
|
||||
popd
|
||||
|
||||
:: --- Unset ------------------------------------------------------------------
|
||||
for %%a in (%*) do set "%%a=0"
|
||||
set raddbg=
|
||||
set compile=
|
||||
set compile_link=
|
||||
set out=
|
||||
set msvc=
|
||||
set debug=
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# --- Unpack Arguments --------------------------------------------------------
|
||||
for arg in "$@"; do declare $arg='1'; done
|
||||
if [[ "$#" == "0" ]]; then raddbg='1'; fi
|
||||
|
||||
cc_sanitize=""
|
||||
if [[ "${asan:-0}" == "1" ]]; then
|
||||
echo "[asan enabled]"
|
||||
cc_sanitize="-fsanitize=address"
|
||||
fi
|
||||
|
||||
# --- Get Current Git Commit Id -----------------------------------------------
|
||||
git_hash=$(git describe --always --dirty)
|
||||
git_hash_full=$(git rev-parse HEAD)
|
||||
|
||||
# --- Compile/Link Line Definitions -------------------------------------------
|
||||
cc_cflags_gcc=""
|
||||
cc_cflags_clang=${cc_sanitize}" -fdiagnostics-absolute-paths -Wno-for-loop-analysis -Wno-incompatible-pointer-types-discards-qualifiers -Wno-initializer-overrides -Wno-compare-distinct-pointer-types -Wno-single-bit-bitfield-constant-conversion -Wno-deprecated-declarations -Wno-writable-strings -Wno-unknown-warning-option -Wno-deprecated-register -Wno-unused-local-typedef -msse2"
|
||||
cc_common="-mcx16 -I../src/ -I../local/ -D_GNU_SOURCE -g -DBUILD_GIT_HASH=\"$git_hash\" -DBUILD_GIT_HASH_FULL=\"$git_hash_full\" -Wall -Wno-missing-braces -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-value -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf"
|
||||
cc_debug="-g -O0 -DBUILD_DEBUG=1 ${cc_common}"
|
||||
cc_release="-g -O2 -DBUILD_DEBUG=0 ${cc_common}"
|
||||
cc_link="-lpthread -lm -lrt -ldl"
|
||||
|
||||
# --- Per-Build Settings ------------------------------------------------------
|
||||
cc_link_dll="-fPIC"
|
||||
|
||||
# --- External Libraries ------------------------------------------------------
|
||||
# sudo apt install -y pkg-config libfreetype6-dev libx11-dev libxext-dev libgl-dev libegl-dev
|
||||
if [[ -x "$(command -v pkg-config)" ]]; then
|
||||
cc_font_provider="$(pkg-config --cflags --libs freetype2)"
|
||||
cc_os_gfx="$(pkg-config --cflags --libs x11 xext)"
|
||||
cc_render="$(pkg-config --cflags --libs gl egl)"
|
||||
else
|
||||
cc_font_provider="-I/usr/include/freetype2 -lfreetype"
|
||||
cc_os_gfx="-lX11 -lXext"
|
||||
cc_render="-lGL -lEGL"
|
||||
fi
|
||||
|
||||
# --- Choose Compile/Link Lines -----------------------------------------------
|
||||
if [[ "${gcc:-0}" == "1" ]]; then compiler="${CC:-gcc} $cc_cflags_gcc"; echo "[gcc compile]";
|
||||
elif [[ "${clang:-1}" == "1" ]]; then compiler="${CC:-clang} $cc_cflags_clang"; echo "[clang compile]";
|
||||
fi
|
||||
if [[ "${release:-0}" == "1" ]]; then echo "[release mode]"; compile="$compiler $cc_release";
|
||||
elif [[ "${debug:-1}" == "1" ]]; then echo "[debug mode]"; compile="$compiler $cc_debug";
|
||||
fi
|
||||
|
||||
# --- Prep Directories --------------------------------------------------------
|
||||
mkdir -p build local
|
||||
|
||||
# --- Build & Run Metaprogram -------------------------------------------------
|
||||
if [[ "${no_meta:-0}" == "0" ]]
|
||||
then
|
||||
echo "[building metagen]"
|
||||
cd build
|
||||
$compiler $cc_debug ../src/metagen/metagen_main.c $cc_link -o metagen
|
||||
./metagen
|
||||
cd ..
|
||||
fi
|
||||
|
||||
# --- Build Everything (@build_targets) ---------------------------------------
|
||||
cd build
|
||||
if [[ "${raddbg:-0}" == "1" ]]; then didbuild=1 && $compile ../src/raddbg/raddbg_main.c $cc_link $cc_os_gfx $cc_render $cc_font_provider -o raddbg; fi
|
||||
if [[ "${raddbg_non_graphical:-0}" == "1" ]]; then didbuild=1 && $compile ../src/raddbg/raddbg_main.c -DWM_STUB=1 -DR_BACKEND=R_BACKEND_STUB $cc_link $cc_os_gfx $cc_render $cc_font_provider -o raddbg_non_graphical; fi
|
||||
if [[ "${radbin:-0}" == "1" ]]; then didbuild=1 && $compile ../src/radbin/radbin_main.c $cc_link -o radbin; fi
|
||||
if [[ "${radlink:-0}" == "1" ]]; then didbuild=1 && $compile ../src/linker/lnk.c $cc_link -o radlink; fi
|
||||
if [[ "${torture:-0}" == "1" ]]; then didbuild=1 && $compile ../src/torture/torture_main.c $cc_link $cc_os_gfx $cc_render $cc_font_provider -o torture; fi
|
||||
cd ..
|
||||
|
||||
# --- Warn On No Builds -------------------------------------------------------
|
||||
if [[ "${didbuild:-0}" == "0" ]]
|
||||
then
|
||||
echo "[WARNING] no valid build target specified; must use build target names as arguments to this script, like \`./build.sh raddbg\` or \`./build.sh radlink\`."
|
||||
exit 1
|
||||
fi
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,93 +0,0 @@
|
||||
{
|
||||
"version": "0.2.1",
|
||||
"defaults": {},
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cppdbg",
|
||||
"name": "launch - torture",
|
||||
"request": "launch",
|
||||
"project": "c://windows//system32//cmd.exe",
|
||||
"cwd": "build/",
|
||||
"program": "torture",
|
||||
"args": [ "d2r_checksums" ],
|
||||
"stopAtEntry": false,
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "wsl",
|
||||
"pipeArgs": [ "${debuggerCommand}" ]
|
||||
},
|
||||
"setupCommands": [
|
||||
{ "text": "-gdb-set follow-fork-mode parent" },
|
||||
{ "text": "-gdb-set detach-on-fork on" },
|
||||
{ "text": "handle SIGUSR1 nostop" }
|
||||
],
|
||||
"MIMode": "gdb"
|
||||
},
|
||||
{
|
||||
"type": "cppdbg",
|
||||
"name": "launch - radbin",
|
||||
"request": "launch",
|
||||
"project": "c://windows//system32//cmd.exe",
|
||||
"cwd": "build/",
|
||||
"program": "radbin",
|
||||
"args": [ "-voff2line", "-voff:0x1000", "torture_artifacts/d2r_line_table/a.rdi" ],
|
||||
"stopAtEntry": false,
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "wsl",
|
||||
"pipeArgs": [ "${debuggerCommand}" ]
|
||||
},
|
||||
"setupCommands": [
|
||||
{ "text": "-gdb-set follow-fork-mode parent" },
|
||||
{ "text": "-gdb-set detach-on-fork on" },
|
||||
{ "text": "handle SIGUSR1 nostop" }
|
||||
],
|
||||
"MIMode": "gdb"
|
||||
},
|
||||
{
|
||||
"type": "cppdbg",
|
||||
"name": "launch - radlink",
|
||||
"request": "launch",
|
||||
"project": "c://windows//system32//cmd.exe",
|
||||
"cwd": "/mnt/c/devel/raddebugger/build/torture_artifacts/debug_p_sig_mismatch",
|
||||
"program": "/mnt/c/devel/raddebugger/build/radlink",
|
||||
"args": [ "/subsystem:console", "/entry:entry", "/out:a.exe", "/debug:full", "a.obj", "b.obj", "entry.obj" ],
|
||||
"stopAtEntry": false,
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "wsl",
|
||||
"pipeArgs": [ "${debuggerCommand}" ]
|
||||
},
|
||||
"setupCommands": [
|
||||
{ "text": "handle SIGUSR1 nostop" }
|
||||
],
|
||||
"MIMode": "gdb"
|
||||
},
|
||||
{
|
||||
"type": "cppdbg",
|
||||
"name": "launch - raddbg",
|
||||
"request": "launch",
|
||||
"project": "c://windows//system32//cmd.exe",
|
||||
"cwd": "build/",
|
||||
"program": "/mnt/c/devel/raddebugger/build/raddbg",
|
||||
//"processId": "65821",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "wsl",
|
||||
"pipeArgs": [ "${debuggerCommand}" ]
|
||||
},
|
||||
"setupCommands": [
|
||||
{ "text": "handle SIGUSR1 nostop" }
|
||||
],
|
||||
"MIMode": "gdb"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,73 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DWARF_DUMP_H
|
||||
#define DWARF_DUMP_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DW_DumperCacheFlag_InfoRanges = (1 << 0),
|
||||
DW_DumperCacheFlag_CuArr = (1 << 1),
|
||||
DW_DumperCacheFlag_LineVmMap = (1 << 2),
|
||||
DW_DumperCacheFlag_TagTrees = (1 << 3),
|
||||
} DW_DumperCacheFlags;
|
||||
|
||||
typedef struct DW_Dumper
|
||||
{
|
||||
Arena *arena;
|
||||
Arch arch;
|
||||
B32 is_relaxed;
|
||||
B32 is_verbose;
|
||||
DW_Raw *input;
|
||||
DW_DumperCacheFlags cache_flags;
|
||||
struct {
|
||||
Rng1U64Array info_ranges;
|
||||
DW_CompUnit *cu_arr;
|
||||
HashTable *line_vm_map;
|
||||
DW_TagTree *tag_trees;
|
||||
} cache;
|
||||
} DW_Dumper;
|
||||
|
||||
#define DW_PRINTER(name) void name(Arena *arena, String8List *strings, int indent, DW_Dumper *dumper)
|
||||
typedef DW_PRINTER(DW_Printer);
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
// cache
|
||||
internal Rng1U64Array dw_get_info_ranges(DW_Dumper *dumper);
|
||||
internal DW_CompUnit * dw_get_cu_arr(DW_Dumper *dumper);
|
||||
internal HashTable * dw_get_line_vm_map(DW_Dumper *dumper);
|
||||
internal DW_TagTree * dw_get_tag_trees(DW_Dumper *dumper);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Stringification Helpers
|
||||
|
||||
internal String8List dw_string_list_from_cfi_program(Arena *arena, U64 cu_base, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format, U64 pc_begin, DW_CIE *cie, DW_DecodePtr *decode_ptr_func, void *deocde_ptr_ud, String8 program);
|
||||
internal String8List dw_string_list_from_expression (Arena *arena, String8 raw_data, U64 cu_base, U64 addr_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
|
||||
|
||||
internal String8 dw_string_from_reg_off (Arena *arena, Arch arch, DW_Reg reg_idx, S64 reg_off);
|
||||
internal String8 dw_string_from_attrib_value(Arena *arena, DW_Raw *input, Arch arch, DW_CompUnit *cu, DW_LineVM *line_vm, DW_Attrib *attrib);
|
||||
internal String8 dw_string_from_expression (Arena *arena, String8 expr, U64 cu_base, U64 addr_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
|
||||
internal String8 dw_string_from_cfa (Arena *arena, Arch arch, U64 address_size, DW_Version version, DW_Ext ext, DW_Format format, DW_CFA cfa);
|
||||
internal String8 dw_string_from_cfi_row (Arena *arena, Arch arch, U64 address_size, DW_Version version, DW_Ext ext, DW_Format format, DW_CFI_Row *row);
|
||||
|
||||
#if 0
|
||||
internal void dw_print_eh_frame (Arena *arena, String8List *out, String8 indent, String8 raw_eh_frame, Arch arch, DW_Version ver, DW_Ext ext, EH_PtrCtx *ptr_ctx);
|
||||
internal void dw_print_debug_loc (Arena *arena, String8List *out, String8 indent, DW_Raw *input, Arch arch, ExecutableImageKind image_type, B32 relaxed);
|
||||
internal void dw_print_debug_ranges (Arena *arena, String8List *out, String8 indent, DW_Raw *input, Arch arch, ExecutableImageKind image_type, B32 relaxed);
|
||||
internal void dw_print_debug_aranges (Arena *arena, String8List *out, String8 indent, DW_Raw *input);
|
||||
internal void dw_print_debug_addr (Arena *arena, String8List *out, String8 indent, DW_Raw *input);
|
||||
internal void dw_print_debug_loclists (Arena *arena, String8List *out, String8 indent, DW_Raw *input, Rng1U64Array segment_vranges, Arch arch);
|
||||
internal void dw_print_debug_rnglists (Arena *arena, String8List *out, String8 indent, DW_Raw *input, Rng1U64Array segment_vranges);
|
||||
internal void dw_print_debug_pubnames (Arena *arena, String8List *out, String8 indent, DW_Raw *input);
|
||||
internal void dw_print_debug_pubtypes (Arena *arena, String8List *out, String8 indent, DW_Raw *input);
|
||||
internal void dw_print_debug_line_str (Arena *arena, String8List *out, String8 indent, DW_Raw *input);
|
||||
internal void dw_print_debug_str_offsets(Arena *arena, String8List *out, String8 indent, DW_Raw *input);
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Dump Entry Point
|
||||
|
||||
internal String8List dw_dump_list_from_sections(Arena *arena, DW_Raw *input, Arch arch, DW_SectionFlags dump_sections, B32 is_verbose);
|
||||
|
||||
#endif // DWARF_DUMP_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,233 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DWARF_EXPR_H
|
||||
#define DWARF_EXPR_H
|
||||
|
||||
////////////////////////////////
|
||||
// evaluator
|
||||
|
||||
#define DW_ExprValueType_IsSigned(x) ((x) == DW_ExprValueType_S8 || (x) == DW_ExprValueType_S16 || (x) == DW_ExprValueType_S32 || (x) == DW_ExprValueType_S64 || (x) == DW_ExprValueType_S128 || (x) == DW_ExprValueType_S256 || (x) == DW_ExprValueType_S512)
|
||||
#define DW_ExprValueType_IsUnsigned(x) ((x) == DW_ExprValueType_U8 || (x) == DW_ExprValueType_U16 || (x) == DW_ExprValueType_U32 || (x) == DW_ExprValueType_U64 || (x) == DW_ExprValueType_U128 || (x) == DW_ExprValueType_U256 || (x) == DW_ExprValueType_U512)
|
||||
#define DW_ExprValueType_IsFloat(x) ((x) == DW_ExprValueType_F32 || (x) == DW_ExprValueType_F64)
|
||||
#define DW_ExprValueType_IsInt(x) (DW_ExprValueType_IsSigned(x) || DW_ExprValueType_IsUnsigned(x) || (x) == DW_ExprValueType_Addr)
|
||||
typedef enum
|
||||
{
|
||||
DW_ExprValueType_Generic,
|
||||
DW_ExprValueType_U8,
|
||||
DW_ExprValueType_U16,
|
||||
DW_ExprValueType_U32,
|
||||
DW_ExprValueType_U64,
|
||||
DW_ExprValueType_U128,
|
||||
DW_ExprValueType_U256,
|
||||
DW_ExprValueType_U512,
|
||||
DW_ExprValueType_S8,
|
||||
DW_ExprValueType_S16,
|
||||
DW_ExprValueType_S32,
|
||||
DW_ExprValueType_S64,
|
||||
DW_ExprValueType_S128,
|
||||
DW_ExprValueType_S256,
|
||||
DW_ExprValueType_S512,
|
||||
DW_ExprValueType_F32,
|
||||
DW_ExprValueType_F64,
|
||||
DW_ExprValueType_Addr,
|
||||
DW_ExprValueType_Implicit,
|
||||
DW_ExprValueType_Bool,
|
||||
} DW_ExprValueType;
|
||||
|
||||
typedef S8 DW_ExprBool;
|
||||
|
||||
typedef struct DW_ExprValue
|
||||
{
|
||||
DW_ExprValueType type;
|
||||
Rng1U64Array ranges;
|
||||
U64 *offsets;
|
||||
union {
|
||||
U8 u8;
|
||||
U16 u16;
|
||||
U32 u32;
|
||||
U64 u64;
|
||||
U128 u128;
|
||||
U256 u256;
|
||||
U512 u512;
|
||||
S8 s8;
|
||||
S16 s16;
|
||||
S32 s32;
|
||||
S64 s64;
|
||||
F32 f32;
|
||||
F64 f64;
|
||||
U64 addr;
|
||||
DW_ExprBool boolean;
|
||||
String8 implicit;
|
||||
String8 generic;
|
||||
};
|
||||
} DW_ExprValue;
|
||||
|
||||
typedef struct DW_ExprValueNode
|
||||
{
|
||||
DW_ExprValue v;
|
||||
struct DW_ExprValueNode *next;
|
||||
} DW_ExprValueNode;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DW_PieceKind_Null,
|
||||
DW_PieceKind_Value,
|
||||
DW_PieceKind_Undefined,
|
||||
} DW_PieceKind;
|
||||
|
||||
typedef struct DW_Piece
|
||||
{
|
||||
DW_PieceKind kind;
|
||||
U64 bit_size;
|
||||
U8 *value;
|
||||
} DW_Piece;
|
||||
|
||||
typedef struct DW_PieceNode
|
||||
{
|
||||
DW_Piece v;
|
||||
struct DW_PieceNode *next;
|
||||
} DW_PieceNode;
|
||||
|
||||
typedef struct DW_PieceList
|
||||
{
|
||||
U64 count;
|
||||
DW_PieceNode *first;
|
||||
DW_PieceNode *last;
|
||||
} DW_PieceList;
|
||||
|
||||
typedef struct DW_ExprStack
|
||||
{
|
||||
U64 count;
|
||||
DW_ExprValueNode *top;
|
||||
} DW_ExprStack;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DW_ExprEvalResult_Fail,
|
||||
DW_ExprEvalResult_Ok,
|
||||
DW_ExprEvalResult_Maybe,
|
||||
DW_ExprEvalResult_ExecOpLimitReached,
|
||||
} DW_ExprEvalResult;
|
||||
|
||||
////////////////////////////////
|
||||
// encoder
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DW_ExprEncType_Null,
|
||||
DW_ExprEncType_Op,
|
||||
DW_ExprEncType_U8,
|
||||
DW_ExprEncType_U16,
|
||||
DW_ExprEncType_U32,
|
||||
DW_ExprEncType_U64,
|
||||
DW_ExprEncType_S8,
|
||||
DW_ExprEncType_S16,
|
||||
DW_ExprEncType_S32,
|
||||
DW_ExprEncType_S64,
|
||||
DW_ExprEncType_ULEB128,
|
||||
DW_ExprEncType_SLEB128,
|
||||
DW_ExprEncType_Addr,
|
||||
DW_ExprEncType_Block,
|
||||
DW_ExprEncType_DwarfUInt,
|
||||
DW_ExprEncType_Label,
|
||||
DW_ExprEncType_DeclLabel,
|
||||
} DW_ExprEncType;
|
||||
|
||||
typedef struct DW_ExprEnc
|
||||
{
|
||||
DW_ExprEncType type;
|
||||
union {
|
||||
DW_ExprOp op;
|
||||
U8 u8;
|
||||
U16 u16;
|
||||
U32 u32;
|
||||
U64 u64;
|
||||
S8 s8;
|
||||
S16 s16;
|
||||
S32 s32;
|
||||
S64 s64;
|
||||
U64 addr;
|
||||
String8 block;
|
||||
String8 label;
|
||||
};
|
||||
} DW_ExprEnc;
|
||||
|
||||
#define DW_ExprEnc_Op(v) { .type = DW_ExprEncType_Op, .op = DW_ExprOp_##v }
|
||||
#define DW_ExprEnc_U8(v) { .type = DW_ExprEncType_U8, .u8 = v }
|
||||
#define DW_ExprEnc_U16(v) { .type = DW_ExprEncType_U16, .u16 = v }
|
||||
#define DW_ExprEnc_U32(v) { .type = DW_ExprEncType_U32, .u32 = v }
|
||||
#define DW_ExprEnc_U64(v) { .type = DW_ExprEncType_U64, .u64 = v }
|
||||
#define DW_ExprEnc_S8(v) { .type = DW_ExprEncType_S8, .s8 = v }
|
||||
#define DW_ExprEnc_S16(v) { .type = DW_ExprEncType_S16, .s16 = v }
|
||||
#define DW_ExprEnc_S32(v) { .type = DW_ExprEncType_S32, .s32 = v }
|
||||
#define DW_ExprEnc_S64(v) { .type = DW_ExprEncType_S64, .s64 = v }
|
||||
#define DW_ExprEnc_ULEB128(v) { .type = DW_ExprEncType_ULEB128, .u64 = v }
|
||||
#define DW_ExprEnc_SLEB128(v) { .type = DW_ExprEncType_SLEB128, .s64 = v }
|
||||
#define DW_ExprEnc_Addr(v) { .type = DW_ExprEncType_Addr, .addr = v }
|
||||
#define DW_ExprEnc_Block(v) { .type = DW_ExprEncType_Block, .block = v }
|
||||
#define DW_ExprEnc_UInt(v) { .type = DW_ExprEncType_DwarfUInt, .u64 = v }
|
||||
#define DW_ExprEnc_Label(v) { .type = DW_ExprEncType_Label, .label = str8_lit_comp(v) }
|
||||
#define DW_ExprEnc_DeclLabel(v) { .type = DW_ExprEncType_DeclLabel, .label = str8_lit_comp(v) }
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
// pieces
|
||||
internal DW_PieceNode * dw_piece_list_push(Arena *arena, DW_PieceList *list, DW_Piece v);
|
||||
|
||||
// size -> type
|
||||
internal DW_ExprValueType dw_expr_unsigned_value_type_from_bit_size(U64 bit_size);
|
||||
internal DW_ExprValueType dw_expr_signed_value_type_from_bit_size(U64 bit_size);
|
||||
internal DW_ExprValueType dw_expr_float_type_from_bit_size(U64 bit_size);
|
||||
|
||||
// type -> size
|
||||
internal U64 dw_expr_byte_size_from_value_type(U64 addr_size, DW_ExprValueType k);
|
||||
|
||||
// typer
|
||||
internal DW_ExprValueType dw_expr_pick_common_value_type(DW_ExprValueType rhs, DW_ExprValueType lhs);
|
||||
internal DW_ExprValueType dw_expr_pick_common_compar_value_type(DW_ExprValueType rhs, DW_ExprValueType lhs);
|
||||
internal DW_ExprValue dw_expr_cast(DW_ExprValue value, DW_ExprValueType type);
|
||||
|
||||
// arithmetic operators
|
||||
internal DW_ExprValue dw_expr_add (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_minus(DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_mul (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_div (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_mod (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
|
||||
// comparison operators
|
||||
internal DW_ExprValue dw_expr_eq(DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_ge(DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_gt(DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_le(DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_lt(DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_ne(DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
|
||||
// bitwise operators
|
||||
internal DW_ExprValue dw_expr_xor (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_and (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_or (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_shl (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_shr (DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
internal DW_ExprValue dw_expr_shra(DW_ExprValue rhs, DW_ExprValue lhs);
|
||||
|
||||
// unary operators
|
||||
internal DW_ExprValue dw_expr_abs(DW_ExprValue value);
|
||||
internal DW_ExprValue dw_expr_neg(DW_ExprValue value);
|
||||
internal DW_ExprValue dw_expr_not(DW_ExprValue value);
|
||||
|
||||
// stack
|
||||
internal DW_ExprValueNode * dw_expr_stack_push(Arena *arena, DW_ExprStack *stack, DW_ExprValue value);
|
||||
internal DW_ExprValueNode * dw_expr_stack_push_unsigned(Arena *arena, DW_ExprStack *stack, void *value, U64 value_size);
|
||||
internal DW_ExprValue dw_expr_stack_pop(DW_ExprStack *stack);
|
||||
internal DW_ExprValue dw_expr_stack_peek(DW_ExprStack *stack);
|
||||
internal DW_ExprValueNode * dw_expr_stack_pick(DW_ExprStack *stack, U64 idx);
|
||||
|
||||
// value helpers
|
||||
internal String8 dw_string_from_expr_value(Arena *arena, U64 addr_size, DW_ExprValue v);
|
||||
|
||||
// evaluator
|
||||
internal DW_ExprEvalResult dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 frame_base, U64 cfa, U64 tls_base, U64 op_limit, DW_Expr expr, void *reg_block, MachineOp_MemRead *mem_read, void *mem_read_ud, DW_ExprValue *value_out);
|
||||
|
||||
#endif //DWARF_EXPR_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,528 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DWARF_PARSE_H
|
||||
#define DWARF_PARSE_H
|
||||
|
||||
typedef struct DW_Section
|
||||
{
|
||||
String8 name;
|
||||
String8 data;
|
||||
B32 is_dwo;
|
||||
} DW_Section;
|
||||
|
||||
typedef struct DW_Raw
|
||||
{
|
||||
DW_Section sec[DW_Section_COUNT];
|
||||
DW_Section sup[DW_Section_COUNT];
|
||||
} DW_Raw;
|
||||
|
||||
typedef struct DW_ListUnit
|
||||
{
|
||||
DW_Version version;
|
||||
U64 address_size;
|
||||
U64 segment_selector_size;
|
||||
U64 entry_size;
|
||||
String8 entries;
|
||||
} DW_ListUnit;
|
||||
|
||||
typedef struct DW_ListUnitInput
|
||||
{
|
||||
U64 addr_count;
|
||||
U64 str_offset_count;
|
||||
U64 rnglist_count;
|
||||
U64 loclist_count;
|
||||
Rng1U64Array addr_ranges;
|
||||
Rng1U64Array str_offset_ranges;
|
||||
Rng1U64Array rnglist_ranges;
|
||||
Rng1U64Array loclist_ranges;
|
||||
DW_ListUnit *addrs;
|
||||
DW_ListUnit *str_offsets;
|
||||
DW_ListUnit *rnglists;
|
||||
DW_ListUnit *loclists;
|
||||
} DW_ListUnitInput;
|
||||
|
||||
typedef struct DW_AbbrevTableEntry
|
||||
{
|
||||
U64 id;
|
||||
U64 off;
|
||||
} DW_AbbrevTableEntry;
|
||||
|
||||
typedef struct DW_AbbrevTable DW_AbbrevTable;
|
||||
struct DW_AbbrevTable
|
||||
{
|
||||
U64 count;
|
||||
DW_AbbrevTableEntry *entries;
|
||||
};
|
||||
|
||||
typedef enum DW_AbbrevKind
|
||||
{
|
||||
DW_Abbrev_Null,
|
||||
DW_Abbrev_Tag,
|
||||
DW_Abbrev_Attrib,
|
||||
DW_Abbrev_AttribSequenceEnd,
|
||||
DW_Abbrev_DIEBegin,
|
||||
DW_Abbrev_DIEEnd,
|
||||
} DW_AbbrevKind;
|
||||
|
||||
typedef U32 DW_AbbrevFlags;
|
||||
enum
|
||||
{
|
||||
DW_AbbrevFlag_HasImplicitConst = (1 << 0),
|
||||
DW_AbbrevFlag_HasChildren = (1 << 1),
|
||||
};
|
||||
|
||||
typedef struct DW_Abbrev
|
||||
{
|
||||
DW_AbbrevKind kind;
|
||||
U64 sub_kind;
|
||||
U64 id;
|
||||
S64 implicit_const;
|
||||
DW_AbbrevFlags flags;
|
||||
} DW_Abbrev;
|
||||
|
||||
typedef struct DW_Attrib
|
||||
{
|
||||
U64 info_off;
|
||||
U64 abbrev_off;
|
||||
U64 abbrev_id;
|
||||
DW_AttribKind attrib_kind;
|
||||
DW_Form form;
|
||||
} DW_Attrib;
|
||||
|
||||
typedef struct DW_AttribNode
|
||||
{
|
||||
struct DW_AttribNode *next;
|
||||
DW_Attrib v;
|
||||
} DW_AttribNode;
|
||||
|
||||
typedef struct DW_AttribList
|
||||
{
|
||||
DW_AttribNode *first;
|
||||
DW_AttribNode *last;
|
||||
U64 count;
|
||||
} DW_AttribList;
|
||||
|
||||
typedef U8 DW_TagSpare;
|
||||
typedef struct DW_Tag
|
||||
{
|
||||
B32 has_children;
|
||||
U64 abbrev_id;
|
||||
DW_TagKind kind;
|
||||
DW_AttribList attribs;
|
||||
U64 info_off;
|
||||
DW_TagSpare v;
|
||||
} DW_Tag;
|
||||
|
||||
typedef struct DW_TagNode
|
||||
{
|
||||
DW_Tag tag;
|
||||
struct DW_TagNode *sibling;
|
||||
struct DW_TagNode *first_child;
|
||||
struct DW_TagNode *last_child;
|
||||
} DW_TagNode;
|
||||
|
||||
typedef struct DW_Loc
|
||||
{
|
||||
Rng1U64 range;
|
||||
String8 expr;
|
||||
} DW_Loc;
|
||||
|
||||
typedef struct DW_LocNode
|
||||
{
|
||||
DW_Loc v;
|
||||
struct DW_LocNode *next;
|
||||
} DW_LocNode;
|
||||
|
||||
typedef struct DW_LocList
|
||||
{
|
||||
U64 count;
|
||||
DW_LocNode *first;
|
||||
DW_LocNode *last;
|
||||
} DW_LocList;
|
||||
|
||||
typedef struct DW_CompUnit
|
||||
{
|
||||
B32 relaxed;
|
||||
DW_Ext ext;
|
||||
DW_CompUnitKind kind;
|
||||
DW_Version version;
|
||||
DW_Format format;
|
||||
U64 address_size;
|
||||
U64 abbrev_off;
|
||||
Rng1U64 info_range;
|
||||
U64 first_tag_info_off;
|
||||
DW_AbbrevTable abbrev_table;
|
||||
DW_ListUnit *addr_lu;
|
||||
DW_ListUnit *str_offsets_lu;
|
||||
DW_ListUnit *rnglists_lu;
|
||||
DW_ListUnit *loclists_lu;
|
||||
U64 low_pc;
|
||||
U64 dwo_id;
|
||||
DW_Tag tag;
|
||||
HashTable *tag_ht;
|
||||
} DW_CompUnit;
|
||||
|
||||
typedef struct DW_TagTree
|
||||
{
|
||||
DW_TagNode *root;
|
||||
U64 tag_count;
|
||||
} DW_TagTree;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
typedef struct DW_LineFile
|
||||
{
|
||||
String8 path;
|
||||
U64 dir_idx;
|
||||
U64 time_stamp;
|
||||
U64 size;
|
||||
U128 md5;
|
||||
String8 source;
|
||||
} DW_LineFile;
|
||||
|
||||
typedef struct DW_LineFileNode
|
||||
{
|
||||
struct DW_LineFileNode *next;
|
||||
DW_LineFile file;
|
||||
} DW_LineFileNode;
|
||||
|
||||
typedef struct DW_LineFileList
|
||||
{
|
||||
U64 node_count;
|
||||
DW_LineFileNode *first;
|
||||
DW_LineFileNode *last;
|
||||
} DW_LineFileList;
|
||||
|
||||
typedef struct DW_LineFileArray
|
||||
{
|
||||
U64 count;
|
||||
DW_LineFile *v;
|
||||
} DW_LineFileArray;
|
||||
|
||||
typedef struct DW_LineVMHeader
|
||||
{
|
||||
U64 unit_length;
|
||||
DW_Version version;
|
||||
U8 address_size; // Duplicates size from the compilation unit but is needed to support stripped exe that just have .debug_line and .debug_line_str.
|
||||
U8 segment_selector_size;
|
||||
U64 header_length;
|
||||
U8 min_inst_len;
|
||||
U8 max_ops_for_inst;
|
||||
U8 default_is_stmt;
|
||||
S8 line_base;
|
||||
U8 line_range;
|
||||
U8 opcode_base;
|
||||
U64 num_opcode_lens;
|
||||
U8 *opcode_lens;
|
||||
U64 line_program_off;
|
||||
String8Array dir_table;
|
||||
DW_LineFileArray file_table;
|
||||
} DW_LineVMHeader;
|
||||
|
||||
typedef struct DW_LineVMState
|
||||
{
|
||||
U64 address; // Address of a machine instruction.
|
||||
U32 op_index; // This is used by the VLIW instructions to indicate index of operation inside the instruction.
|
||||
|
||||
// Line table doesn't contain full path to a file, instead
|
||||
// DWARF encodes path as two indices. First index will point into a directory
|
||||
// table, and second points into a file name table.
|
||||
U64 file_index;
|
||||
|
||||
U64 line;
|
||||
U64 column;
|
||||
|
||||
B32 is_stmt; // Indicates that "address" points to place suitable for a breakpoint.
|
||||
B32 basic_block; // Indicates that the "address" is inside a basic block.
|
||||
|
||||
// Indicates that "address" points to place where function starts.
|
||||
// Usually prologue is the place where compiler emits instructions to
|
||||
// prepare stack for a function.
|
||||
B32 prologue_end;
|
||||
|
||||
B32 epilogue_begin; // Indicates that "address" points to section where function exits and unwinds stack.
|
||||
U64 isa; // Instruction set that is used.
|
||||
U64 discriminator; // Arbitrary id that indicates to which block these instructions belong.
|
||||
B32 end_sequence; // Indicates that "address" points to the first instruction in the instruction block that follows.
|
||||
} DW_LineVMState;
|
||||
|
||||
typedef struct DW_LineVM
|
||||
{
|
||||
Arena *arena;
|
||||
U64 cursor;
|
||||
String8 program;
|
||||
DW_LineVMHeader header;
|
||||
B32 new_line;
|
||||
B32 new_seq;
|
||||
B32 skip_opcode;
|
||||
DW_LineVMState state;
|
||||
U64 advance;
|
||||
U64 opcode_off;
|
||||
DW_StdOpcode opcode;
|
||||
DW_ExtOpcode ext_opcode;
|
||||
U64 ext_length;
|
||||
U64 line_advance;
|
||||
U64 addr_advance;
|
||||
HashTable *ext_file_ht;
|
||||
struct {
|
||||
union {
|
||||
String8 string;
|
||||
U64 u64;
|
||||
S64 s64;
|
||||
};
|
||||
} operands[4];
|
||||
String8 error;
|
||||
} DW_LineVM;
|
||||
|
||||
////////////////////////////////
|
||||
// .debug_pubnames and .debug_pubtypes
|
||||
|
||||
typedef struct DW_PubStringsBucket
|
||||
{
|
||||
struct DW_PubStringsBucket *next;
|
||||
String8 string;
|
||||
U64 info_off;
|
||||
U64 cu_info_off;
|
||||
} DW_PubStringsBucket;
|
||||
|
||||
typedef struct DW_PubStringsTable
|
||||
{
|
||||
U64 size;
|
||||
DW_PubStringsBucket **buckets;
|
||||
} DW_PubStringsTable;
|
||||
|
||||
typedef struct DW_Reference
|
||||
{
|
||||
DW_CompUnit *cu;
|
||||
U64 info_off; // global .debug_info offset
|
||||
} DW_Reference;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Expression
|
||||
|
||||
typedef union DW_ExprOperand
|
||||
{
|
||||
U8 u8;
|
||||
U16 u16;
|
||||
U32 u32;
|
||||
U64 u64;
|
||||
|
||||
S8 s8;
|
||||
S16 s16;
|
||||
S32 s32;
|
||||
S64 s64;
|
||||
|
||||
String8 block;
|
||||
} DW_ExprOperand;
|
||||
|
||||
typedef struct DW_ExprInst
|
||||
{
|
||||
DW_ExprOp opcode;
|
||||
DW_ExprOperand *operands;
|
||||
U64 size;
|
||||
struct DW_ExprInst *next;
|
||||
struct DW_ExprInst *prev;
|
||||
} DW_ExprInst;
|
||||
|
||||
typedef struct DW_Expr
|
||||
{
|
||||
U64 count;
|
||||
DW_ExprInst *first;
|
||||
DW_ExprInst *last;
|
||||
} DW_Expr;
|
||||
|
||||
////////////////////////////////
|
||||
// .debug_frame
|
||||
|
||||
typedef struct DW_CIE
|
||||
{
|
||||
String8 insts;
|
||||
String8 aug_string;
|
||||
String8 aug_data;
|
||||
U64 code_align_factor;
|
||||
S64 data_align_factor;
|
||||
U64 ret_addr_reg;
|
||||
U64 ext[4];
|
||||
DW_Format format;
|
||||
U8 version;
|
||||
U8 address_size;
|
||||
U8 segment_selector_size;
|
||||
} DW_CIE;
|
||||
|
||||
typedef struct DW_FDE
|
||||
{
|
||||
DW_Format format;
|
||||
U64 cie_pointer;
|
||||
Rng1U64 pc_range;
|
||||
String8 insts;
|
||||
} DW_FDE;
|
||||
|
||||
typedef union DW_CFA_Operand
|
||||
{
|
||||
U64 u64;
|
||||
S64 s64;
|
||||
String8 block;
|
||||
} DW_CFA_Operand;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DW_CFA_ParseErrorCode_NewInst,
|
||||
DW_CFA_ParseErrorCode_End,
|
||||
DW_CFA_ParseErrorCode_OutOfData
|
||||
} DW_CFA_ParseErrorCode;
|
||||
|
||||
typedef struct DW_CFA_Inst
|
||||
{
|
||||
DW_CFA_Opcode opcode;
|
||||
DW_CFA_Operand operands[DW_CFA_OperandMax];
|
||||
} DW_CFA_Inst;
|
||||
|
||||
typedef struct DW_CFA_InstNode
|
||||
{
|
||||
DW_CFA_Inst v;
|
||||
struct DW_CFA_InstNode *next;
|
||||
} DW_CFA_InstNode;
|
||||
|
||||
typedef struct DW_CFA_InstList
|
||||
{
|
||||
U64 count;
|
||||
DW_CFA_InstNode *first;
|
||||
DW_CFA_InstNode *last;
|
||||
} DW_CFA_InstList;
|
||||
|
||||
#define DW_DECODE_PTR(name) U64 name(String8 data, void *ud, U64 *ptr_out)
|
||||
typedef DW_DECODE_PTR(DW_DecodePtr);
|
||||
|
||||
// hasher
|
||||
|
||||
internal U64 dw_hash_from_string(String8 string);
|
||||
|
||||
// deserial helpers
|
||||
|
||||
internal U64 str8_deserial_read_dwarf_packed_size(String8 string, U64 off, U64 *size_out);
|
||||
internal U64 str8_deserial_read_dwarf_uint (String8 string, U64 off, DW_Format format, U64 *uint_out);
|
||||
internal U64 str8_deserial_read_uleb128_array(Arena *arena, String8 string, U64 off, U64 count, U64 **arr_out);
|
||||
internal U64 str8_deserial_read_sleb128_array(Arena *arena, String8 string, U64 off, U64 count, S64 **arr_out);
|
||||
|
||||
internal Rng1U64List dw_unit_ranges_from_data(Arena *arena, String8 data);
|
||||
internal Rng1U64Array dw_unit_ranges_from_data_arr(Arena *arena, String8 data);
|
||||
|
||||
// list units
|
||||
|
||||
internal U64 dw_read_list_unit_header_addr (String8 unit_data, DW_ListUnit *lu_out);
|
||||
internal U64 dw_read_list_unit_header_str_offsets(String8 unit_data, DW_ListUnit *lu_out);
|
||||
internal U64 dw_read_list_unit_header_list (String8 unit_data, DW_ListUnit *lu_out);
|
||||
|
||||
internal DW_ListUnitInput dw_list_unit_input_from_input(Arena *arena, DW_Raw *input);
|
||||
|
||||
internal U64 dw_offset_from_list_unit(DW_ListUnit *lu, U64 index);
|
||||
internal U64 dw_addr_from_list_unit (DW_ListUnit *lu, U64 index);
|
||||
|
||||
// abbrev table
|
||||
|
||||
internal U64 dw_read_abbrev_tag (String8 data, U64 offset, DW_Abbrev *out_abbrev);
|
||||
internal U64 dw_read_abbrev_attrib(String8 data, U64 offset, DW_Abbrev *out_abbrev);
|
||||
|
||||
internal DW_AbbrevTable dw_read_abbrev_table(Arena *arena, String8 abbrev_data, U64 abbrev_base);
|
||||
internal U64 dw_abbrev_offset_from_abbrev_id(DW_AbbrevTable table, U64 abbrev_id);
|
||||
|
||||
// form and tag
|
||||
|
||||
internal B32 dw_read_form (String8 data, DW_Version version, DW_Format unit_format, U64 address_size, DW_FormKind form_kind, S64 implicit_const, DW_Form *form_out, U64 *bytes_read_out);
|
||||
internal U64 dw_read_tag (Arena *arena, DW_Raw *input, DW_AbbrevTable abbrev_table, DW_Version version, DW_Format format, U64 address_size, Rng1U64 cu_info_range, U64 tag_info_off, DW_Tag *tag_out);
|
||||
internal U64 dw_read_tag_cu(Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 info_off, DW_Tag *tag_out);
|
||||
|
||||
// attrib interp
|
||||
|
||||
internal U64 dw_interp_sec_offset(DW_Form form);
|
||||
internal String8 dw_interp_exprloc (DW_Form form);
|
||||
internal U128 dw_interp_const_u128(DW_Form form);
|
||||
internal U64 dw_interp_const_u64 (DW_Form form);
|
||||
internal U32 dw_interp_const_u32 (DW_Form form);
|
||||
internal S64 dw_interp_const_s64 (DW_Form form);
|
||||
internal S32 dw_interp_const_s32 (DW_Form form);
|
||||
internal B32 dw_interp_flag (DW_Form form);
|
||||
internal U64 dw_interp_address (U64 address_size, U64 base_addr, DW_ListUnit *addr_xlist, DW_Form form);
|
||||
internal String8 dw_interp_block (DW_Raw *input, DW_CompUnit *cu, DW_Form form);
|
||||
internal String8 dw_interp_string (DW_Raw *input, DW_Format unit_format, DW_ListUnit *str_offsets, DW_Form form);
|
||||
internal String8 dw_interp_line_ptr (DW_Raw *input, DW_Form form);
|
||||
internal DW_LineFile * dw_interp_file (DW_LineVM *line_vm, DW_Form form);
|
||||
internal DW_Reference dw_interp_ref (DW_Raw *input, DW_CompUnit *cu, DW_Form form);
|
||||
internal DW_LocList dw_interp_loclist (Arena *arena, DW_Raw *input, DW_CompUnit *cu, DW_Form form);
|
||||
internal Rng1U64List dw_interp_rnglist (Arena *arena, DW_Raw *input, DW_CompUnit *cu, DW_Form form);
|
||||
|
||||
internal String8 dw_exprloc_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal U128 dw_const_u128_from_attrib(DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal U64 dw_const_u64_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal U32 dw_const_u32_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal S64 dw_const_s64_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal S32 dw_const_s32_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal B32 dw_flag_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal U64 dw_address_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal String8 dw_block_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal String8 dw_string_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal String8 dw_line_ptr_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal DW_LineFile * dw_file_from_attrib (DW_CompUnit *cu, DW_LineVM *line_vm, DW_Attrib *attrib);
|
||||
internal DW_Reference dw_ref_from_attrib (DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal DW_LocList dw_loclist_from_attrib (Arena *arena, DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
internal Rng1U64List dw_rnglist_from_attrib (Arena *arena, DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
|
||||
internal String8 dw_exprloc_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal U128 dw_const_u128_from_tag_attrib_kind(DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal U64 dw_const_u64_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal U32 dw_const_u32_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal B32 dw_flag_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal U64 dw_address_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal String8 dw_block_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal String8 dw_string_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal String8 dw_line_ptr_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal String8 dw_line_ptr_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal DW_LineFile * dw_file_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_LineVM *line_vm, DW_Tag tag, DW_AttribKind kind);
|
||||
internal DW_Reference dw_ref_from_tag_attrib_kind (DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal DW_LocList dw_loclist_from_tag_attrib_kind (Arena *arena, DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
internal Rng1U64List dw_rnglist_from_tag_attrib_kind (Arena *arena, DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
|
||||
internal B32 dw_is_attrib_var_ref(DW_Raw *input, DW_CompUnit *cu, DW_Attrib *attrib);
|
||||
|
||||
// compile unit
|
||||
|
||||
internal DW_CompUnit dw_cu_from_info_off(Arena *arena, DW_Raw *input, DW_ListUnitInput lu_input, U64 offset, B32 relaxed);
|
||||
internal DW_TagTree dw_tag_tree_from_cu(Arena *arena, DW_Raw *input, DW_CompUnit *cu);
|
||||
internal HashTable * dw_make_tag_hash_table(Arena *arena, DW_TagTree tag_tree);
|
||||
internal DW_TagNode * dw_tag_node_from_info_off(DW_CompUnit *cu, U64 info_off);
|
||||
|
||||
// line VM
|
||||
|
||||
internal U64 dw_read_line_vm_header(Arena *arena, DW_Raw *input, String8 cu_stmt_list, String8 cu_dir, String8 cu_name, U8 cu_address_size, DW_ListUnit *cu_str_offsets, DW_LineVMHeader *header_out);
|
||||
internal DW_LineVM * dw_line_vm_init(DW_Raw *input, DW_CompUnit *cu);
|
||||
internal void dw_line_vm_release(DW_LineVM *vm);
|
||||
internal void dw_line_vm_advance(DW_LineVM *vm, U64 advance);
|
||||
internal B32 dw_line_vm_step(DW_LineVM *vm);
|
||||
internal DW_LineFile * dw_line_vm_find_file(DW_LineVM *vm, U64 file_idx);
|
||||
internal String8 dw_path_from_file(Arena *arena, String8Array dir_table, DW_LineFile *file);
|
||||
|
||||
// helper for .debug_pubtypes and .debug_pubnames
|
||||
|
||||
internal DW_PubStringsTable dw_v4_pub_strings_table_from_section_kind(Arena *arena, DW_Raw *input, DW_SectionKind section_kind);
|
||||
|
||||
// expression
|
||||
|
||||
internal DW_Expr dw_expr_from_data(Arena *arena, DW_Format format, U64 addr_size, String8 data);
|
||||
|
||||
// debug frame
|
||||
|
||||
internal void dw_cfa_inst_list_push_node(DW_CFA_InstList *list, DW_CFA_InstNode *n);
|
||||
internal DW_CFA_InstNode * dw_cfa_inst_list_push(Arena *arena, DW_CFA_InstList *list, DW_CFA_Inst v);
|
||||
|
||||
internal U64 dw_read_debug_frame_ptr(String8 data, DW_CIE *cie, U64 *ptr_out);
|
||||
|
||||
internal U64 dw_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc_out);
|
||||
internal B32 dw_parse_cie(String8 data, DW_Format format, Arch arch, DW_CIE *cie_out);
|
||||
internal B32 dw_parse_fde(String8 data, DW_Format format, DW_CIE *cie, DW_FDE *fde_out);
|
||||
internal B32 dw_parse_cfi(String8 data, U64 fde_offset, Arch arch, DW_CIE *cie_out, DW_FDE *fde_out);
|
||||
|
||||
internal DW_CFA_ParseErrorCode dw_parse_cfa_inst(String8 data, U64 code_align_factor, S64 data_align_factor, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud, U64 *bytes_read_out, DW_CFA_Inst *inst_out);
|
||||
internal DW_CFA_InstList dw_parse_cfa_inst_list(Arena *arena, String8 data, U64 code_align_factor, S64 data_align_factor, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud);
|
||||
|
||||
#endif // DWARF_PARSE_H
|
||||
@@ -1,373 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal DW_CFI_Row *
|
||||
dw_make_cfi_row(Arena *arena, U64 reg_count)
|
||||
{
|
||||
DW_CFI_Row *row = push_array(arena, DW_CFI_Row, 1);
|
||||
row->regs = push_array(arena, DW_CFI_Register, reg_count);
|
||||
for EachIndex(reg_idx, reg_count) {
|
||||
row->regs[reg_idx].rule = DW_CFI_RegisterRule_SameValue;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
internal void
|
||||
dw_memcpy_cfi_row(DW_CFI_Row *dst, DW_CFI_Row *src, U64 reg_count)
|
||||
{
|
||||
dst->cfa = src->cfa;
|
||||
MemoryCopyTyped(dst->regs, src->regs, reg_count);
|
||||
}
|
||||
|
||||
internal DW_CFI_Row *
|
||||
dw_copy_cfi_row(Arena *arena, DW_CFI_Row *row, U64 reg_count)
|
||||
{
|
||||
DW_CFI_Row *new_row = dw_make_cfi_row(arena, reg_count);
|
||||
dw_memcpy_cfi_row(new_row, row, reg_count);
|
||||
return new_row;
|
||||
}
|
||||
|
||||
internal DW_CFI_Unwind *
|
||||
dw_cfi_unwind_init(Arena *arena,
|
||||
Arch arch,
|
||||
DW_CIE *cie,
|
||||
DW_FDE *fde,
|
||||
DW_DecodePtr *decode_ptr_func,
|
||||
void *decode_ptr_ud)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
DW_CFI_Unwind *uw = push_array(arena, DW_CFI_Unwind, 1);
|
||||
uw->insts = dw_parse_cfa_inst_list(arena, fde->insts, cie->code_align_factor, cie->data_align_factor, decode_ptr_func, decode_ptr_ud);
|
||||
uw->cie = cie;
|
||||
uw->fde = fde;
|
||||
uw->pc = fde->pc_range.min;
|
||||
uw->arch = arch;
|
||||
uw->reg_count = dw_reg_count_from_arch(arch);
|
||||
|
||||
// setup initial register rules
|
||||
DW_CFA_InstList initial_insts = dw_parse_cfa_inst_list(scratch.arena, cie->insts, cie->code_align_factor, cie->data_align_factor, decode_ptr_func, decode_ptr_ud);
|
||||
uw->row = dw_make_cfi_row(arena, uw->reg_count);
|
||||
uw->curr_inst = initial_insts.first;
|
||||
if (!dw_cfi_next_row(arena, uw)) {
|
||||
AssertAlways(0 && "unable to interpret initial row instructions");
|
||||
}
|
||||
|
||||
// make first row from initial rules
|
||||
uw->initial_row = uw->row;
|
||||
uw->row = dw_copy_cfi_row(arena, uw->initial_row, uw->reg_count);
|
||||
uw->curr_inst = uw->insts.first;
|
||||
|
||||
scratch_end(scratch);
|
||||
return uw;
|
||||
}
|
||||
|
||||
internal B32
|
||||
dw_cfi_next_row(Arena *arena, DW_CFI_Unwind *uw)
|
||||
{
|
||||
B32 is_row_valid = 0;
|
||||
DW_CFA_InstNode *row_inst = uw->curr_inst;
|
||||
|
||||
// skip leading nops
|
||||
while (uw->curr_inst) {
|
||||
if (uw->curr_inst->v.opcode != DW_CFA_Nop) {
|
||||
break;
|
||||
}
|
||||
uw->curr_inst = uw->curr_inst->next;
|
||||
}
|
||||
|
||||
while (uw->curr_inst) {
|
||||
DW_CFA_Inst *inst = &uw->curr_inst->v;
|
||||
|
||||
// validate register operands
|
||||
DW_CFA_OperandType *operand_types = dw_operand_types_from_cfa_op(inst->opcode);
|
||||
U64 operand_count = dw_operand_count_from_cfa_opcode(inst->opcode);
|
||||
for EachIndex(operand_idx, operand_count) {
|
||||
if (operand_types[operand_idx] == DW_CFA_OperandType_Register) {
|
||||
if (inst->operands[operand_idx].u64 >= uw->reg_count) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (inst->opcode) {
|
||||
// Row Creation Instructions
|
||||
case DW_CFA_SetLoc: {
|
||||
uw->pc = inst->operands[0].u64;
|
||||
} break;
|
||||
case DW_CFA_AdvanceLoc:
|
||||
case DW_CFA_AdvanceLoc1:
|
||||
case DW_CFA_AdvanceLoc2:
|
||||
case DW_CFA_AdvanceLoc4: {
|
||||
uw->pc += inst->operands[0].u64;
|
||||
} break;
|
||||
|
||||
// CFA Definition Instructions
|
||||
case DW_CFA_DefCfa:
|
||||
case DW_CFA_DefCfaSf: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
S64 off = inst->operands[1].s64;
|
||||
uw->row->cfa.rule = DW_CFA_Rule_RegOff;
|
||||
uw->row->cfa.reg = reg;
|
||||
uw->row->cfa.off = off;
|
||||
} break;
|
||||
case DW_CFA_DefCfaRegister: {
|
||||
// TODO: report error: this operation is valid only if the current CFA rule is defined to register+offset
|
||||
if (uw->row->cfa.rule != DW_CFA_Rule_RegOff) { goto exit; }
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->cfa.reg = reg;
|
||||
} break;
|
||||
case DW_CFA_DefCfaOffset:
|
||||
case DW_CFA_DefCfaOffsetSf: {
|
||||
// TODO: report error: this operation is valid only if the current CFA rule is defined to register+offset
|
||||
if (uw->row->cfa.rule != DW_CFA_Rule_RegOff) { goto exit; }
|
||||
uw->row->cfa.off = inst->operands[0].s64;
|
||||
} break;
|
||||
case DW_CFA_DefCfaExpr: {
|
||||
uw->row->cfa.rule = DW_CFA_Rule_Expression;
|
||||
uw->row->cfa.expr = inst->operands[0].block;
|
||||
} break;
|
||||
|
||||
// Register Rule Instructions
|
||||
case DW_CFA_Undefined: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->regs[reg].rule = DW_CFI_RegisterRule_Undefined;
|
||||
} break;
|
||||
case DW_CFA_SameValue: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->regs[reg].rule = DW_CFI_RegisterRule_SameValue;
|
||||
} break;
|
||||
case DW_CFA_Offset:
|
||||
case DW_CFA_OffsetExt:
|
||||
case DW_CFA_OffsetExtSf: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->regs[reg].rule = DW_CFI_RegisterRule_Offset;
|
||||
uw->row->regs[reg].n = inst->operands[1].s64;
|
||||
} break;
|
||||
case DW_CFA_ValOffset:
|
||||
case DW_CFA_ValOffsetSf: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->regs[reg].rule = DW_CFI_RegisterRule_ValOffset;
|
||||
uw->row->regs[reg].n = inst->operands[1].s64;
|
||||
} break;
|
||||
case DW_CFA_Register: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->regs[reg].rule = DW_CFI_RegisterRule_Register;
|
||||
uw->row->regs[reg].n = inst->operands[1].s64;
|
||||
} break;
|
||||
case DW_CFA_Expr: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->regs[reg].rule = DW_CFI_RegisterRule_Expression;
|
||||
uw->row->regs[reg].expr = inst->operands[1].block;
|
||||
} break;
|
||||
case DW_CFA_ValExpr: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->regs[reg].rule = DW_CFI_RegisterRule_ValExpression;
|
||||
uw->row->regs[reg].expr = inst->operands[1].block;
|
||||
} break;
|
||||
case DW_CFA_Restore:
|
||||
case DW_CFA_RestoreExt: {
|
||||
U64 reg = inst->operands[0].u64;
|
||||
uw->row->regs[reg] = uw->initial_row->regs[reg];
|
||||
} break;
|
||||
|
||||
// Row State Instructions
|
||||
case DW_CFA_RememberState: {
|
||||
DW_CFI_Row *new_row = dw_copy_cfi_row(arena, uw->row, uw->reg_count);
|
||||
SLLStackPush(uw->row, new_row);
|
||||
} break;
|
||||
case DW_CFA_RestoreState: {
|
||||
if (uw->row == 0) { goto exit; } // TODO: report error: unbalanced number of pushes and pops
|
||||
DW_CFI_Row *free_row = uw->row;
|
||||
SLLStackPop(uw->row);
|
||||
SLLStackPush(uw->free_rows, free_row);
|
||||
} break;
|
||||
|
||||
case DW_CFA_Nop: {} break;
|
||||
|
||||
default: { NotImplemented; } break; // TODO: report error: unknown CFA opcode
|
||||
}
|
||||
|
||||
uw->curr_inst = uw->curr_inst->next;
|
||||
if (uw->curr_inst) {
|
||||
if (dw_is_new_row_cfa_opcode(uw->curr_inst->v.opcode)) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
is_row_valid = row_inst != 0;
|
||||
exit:;
|
||||
return is_row_valid;
|
||||
}
|
||||
|
||||
internal DW_CFI_Row *
|
||||
dw_cfi_row_from_pc(Arena *arena, Arch arch, DW_CIE *cie, DW_FDE *fde, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ctx, U64 pc)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
B32 is_row_found = 0;
|
||||
U64 reg_count = dw_reg_count_from_arch(arch);
|
||||
DW_CFI_Unwind *uw = dw_cfi_unwind_init(scratch.arena, arch, cie, fde, decode_ptr_func, decode_ptr_ctx);
|
||||
DW_CFI_Row *row = dw_copy_cfi_row(scratch.arena, uw->row, uw->reg_count);
|
||||
U64 prev_pc = uw->pc;
|
||||
while (dw_cfi_next_row(scratch.arena, uw)) {
|
||||
if (prev_pc <= pc && pc < uw->pc) {
|
||||
is_row_found = 1;
|
||||
break;
|
||||
}
|
||||
dw_memcpy_cfi_row(row, uw->row, uw->reg_count);
|
||||
prev_pc = uw->pc;
|
||||
}
|
||||
|
||||
// handle last row
|
||||
if (!is_row_found) {
|
||||
if (contains_1u64(fde->pc_range, pc)) {
|
||||
row = uw->row;
|
||||
is_row_found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// copy out final row
|
||||
DW_CFI_Row *result = 0;
|
||||
if (is_row_found) {
|
||||
result = dw_copy_cfi_row(arena, row, reg_count);
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal MachineOpResult
|
||||
dw_compute_cfa(Arch arch, DW_CFI_Row *row, void *reg_block, MachineOp_MemRead *mem_read_func, void *mem_read_ud, U64 *cfa_out)
|
||||
{
|
||||
MachineOpResult unwind_status = MachineOpResult_Fail;
|
||||
ARCH_Info *arch_info = arch_info_from_arch(arch);
|
||||
switch(row->cfa.rule)
|
||||
{
|
||||
case DW_CFA_Rule_Null:{}break;
|
||||
case DW_CFA_Rule_RegOff:
|
||||
{
|
||||
// TODO: report error (invalid register read)
|
||||
U64 cfa_reg_value = 0;
|
||||
U64 reg_size = dw_reg_size_from_code(arch, row->cfa.reg);
|
||||
AssertAlways(reg_size <= sizeof(cfa_reg_value));
|
||||
ARCH_RegCode reg_code = arch_reg_code_from_dw(arch, row->cfa.reg);
|
||||
Rng1U16 reg_rng = arch_info->reg_code_rng_table[reg_code];
|
||||
if(!arch_reg_block_read_range(arch_info, reg_block, reg_rng, &cfa_reg_value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
*cfa_out = cfa_reg_value + row->cfa.off;
|
||||
unwind_status = MachineOpResult_Ok;
|
||||
}break;
|
||||
case DW_CFA_Rule_Expression:
|
||||
{
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
DW_ExprValue value = {0};
|
||||
DW_Expr expr = dw_expr_from_data(scratch.arena, DW_Format_32Bit, byte_size_from_arch(arch), row->cfa.expr);
|
||||
DW_ExprEvalResult eval_result = dw_eval_expr(scratch.arena, arch, DW_Format_32Bit, 0, 0, 0, 9999, expr, reg_block, mem_read_func, mem_read_ud, &value);
|
||||
if (eval_result == DW_ExprEvalResult_Ok) {
|
||||
if (DW_ExprValueType_IsInt(value.type)) {
|
||||
*cfa_out = value.u64;
|
||||
unwind_status = MachineOpResult_Ok;
|
||||
}
|
||||
} else if (eval_result == DW_ExprEvalResult_Maybe) {
|
||||
unwind_status = MachineOpResult_Maybe;
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}break;
|
||||
}
|
||||
return unwind_status;
|
||||
}
|
||||
|
||||
internal MachineOpResult
|
||||
dw_cfi_apply_register_rules(Arch arch, U64 cfa, DW_CFI_Row *row, void *reg_block, MachineOp_MemRead *mem_read_func, void *mem_read_ud)
|
||||
{
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
MachineOpResult unwind_status = MachineOpResult_Ok;
|
||||
|
||||
ARCH_Info *arch_info = arch_info_from_arch(arch);
|
||||
|
||||
U64 max_reg_size = dw_reg_max_size_from_arch(arch);
|
||||
void *reg_buffer = push_array(scratch.arena, U8, max_reg_size);
|
||||
|
||||
U64 reg_count = dw_reg_count_from_arch(arch);
|
||||
for EachIndex(reg_idx, reg_count)
|
||||
{
|
||||
DW_CFI_Register *reg = &row->regs[reg_idx];
|
||||
ARCH_RegCode reg_code = arch_reg_code_from_dw(arch, reg_idx);
|
||||
Rng1U16 reg_rng = arch_info->reg_code_rng_table[reg_code];
|
||||
switch(reg->rule)
|
||||
{
|
||||
default:{}break;
|
||||
case DW_CFI_RegisterRule_Undefined: {} break;
|
||||
case DW_CFI_RegisterRule_SameValue: {} break;
|
||||
case DW_CFI_RegisterRule_Offset:
|
||||
{
|
||||
// read register value from memory
|
||||
U64 addr = cfa + reg->n;
|
||||
U64 reg_size = dw_reg_size_from_code(arch, reg_idx);
|
||||
unwind_status = mem_read_func(addr, reg_buffer, reg_size, mem_read_ud);
|
||||
if (unwind_status != MachineOpResult_Ok) { goto exit; }
|
||||
|
||||
// write register value to the thread context
|
||||
if(!arch_reg_block_write_range(arch_info, reg_block, reg_rng, reg_buffer))
|
||||
{
|
||||
unwind_status = MachineOpResult_Fail;
|
||||
goto exit;
|
||||
}
|
||||
}break;
|
||||
case DW_CFI_RegisterRule_ValOffset:
|
||||
{
|
||||
// compute register value
|
||||
U64 reg_value = cfa + reg->n;
|
||||
|
||||
// write register value to the thread context
|
||||
U64 reg_size = dw_reg_size_from_code(arch, reg_idx);
|
||||
if(!arch_reg_block_write_range(arch_info, reg_block, reg_rng, ®_value))
|
||||
{
|
||||
unwind_status = MachineOpResult_Fail;
|
||||
goto exit;
|
||||
}
|
||||
}break;
|
||||
case DW_CFI_RegisterRule_Register:
|
||||
{
|
||||
// read register value from another register
|
||||
U64 reg_size = dw_reg_size_from_code(arch, reg_idx);
|
||||
if(!arch_reg_block_read_range(arch_info, reg_block, reg_rng, reg_buffer))
|
||||
{
|
||||
unwind_status = MachineOpResult_Fail;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// write register value to the thread context
|
||||
if(!arch_reg_block_write_range(arch_info, reg_block, reg_rng, reg_buffer))
|
||||
{
|
||||
unwind_status = MachineOpResult_Fail;
|
||||
goto exit;
|
||||
}
|
||||
if (unwind_status != MachineOpResult_Ok) { goto exit; }
|
||||
}break;
|
||||
case DW_CFI_RegisterRule_Expression:
|
||||
{
|
||||
// TODO: evaluate expression
|
||||
NotImplemented;
|
||||
}break;
|
||||
case DW_CFI_RegisterRule_ValExpression:
|
||||
{
|
||||
// TODO: evaluate expression
|
||||
NotImplemented;
|
||||
}break;
|
||||
case DW_CFI_RegisterRule_Architectural:
|
||||
{
|
||||
NotImplemented;
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
// update stack pointer
|
||||
arch_reg_block_write_sp(arch_info, reg_block, cfa);
|
||||
|
||||
exit:;
|
||||
scratch_end(scratch);
|
||||
return unwind_status;
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DWARF_UNWIND_H
|
||||
#define DWARF_UNWIND_H
|
||||
|
||||
typedef enum DW_CFI_RegisterRule
|
||||
{
|
||||
DW_CFI_RegisterRule_Undefined,
|
||||
DW_CFI_RegisterRule_SameValue,
|
||||
DW_CFI_RegisterRule_Offset,
|
||||
DW_CFI_RegisterRule_ValOffset,
|
||||
DW_CFI_RegisterRule_Register,
|
||||
DW_CFI_RegisterRule_Expression,
|
||||
DW_CFI_RegisterRule_ValExpression,
|
||||
DW_CFI_RegisterRule_Architectural
|
||||
} DW_CFI_RegisterRule;
|
||||
|
||||
typedef enum DW_CFA_Rule
|
||||
{
|
||||
DW_CFA_Rule_Null,
|
||||
DW_CFA_Rule_RegOff,
|
||||
DW_CFA_Rule_Expression
|
||||
} DW_CFA_Rule;
|
||||
|
||||
typedef struct DW_CFA
|
||||
{
|
||||
DW_CFA_Rule rule;
|
||||
union {
|
||||
struct {
|
||||
DW_Reg reg;
|
||||
S64 off;
|
||||
};
|
||||
String8 expr;
|
||||
};
|
||||
} DW_CFA;
|
||||
|
||||
typedef struct DW_CFI_Register
|
||||
{
|
||||
DW_CFI_RegisterRule rule;
|
||||
union {
|
||||
S64 n;
|
||||
String8 expr;
|
||||
};
|
||||
} DW_CFI_Register;
|
||||
|
||||
typedef struct DW_CFI_Row
|
||||
{
|
||||
DW_CFA cfa;
|
||||
DW_CFI_Register *regs;
|
||||
struct DW_CFI_Row *next;
|
||||
} DW_CFI_Row;
|
||||
|
||||
typedef struct DW_CFI_Unwind
|
||||
{
|
||||
DW_CFA_InstList insts;
|
||||
DW_CIE *cie;
|
||||
DW_FDE *fde;
|
||||
DW_CFI_Row *initial_row;
|
||||
DW_CFI_Row *row;
|
||||
DW_CFA_InstNode *curr_inst;
|
||||
DW_CFI_Row *free_rows;
|
||||
U64 pc;
|
||||
Arch arch;
|
||||
U64 reg_count;
|
||||
} DW_CFI_Unwind;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal DW_CFI_Row * dw_make_cfi_row(Arena *arena, U64 reg_count);
|
||||
internal DW_CFI_Row * dw_copy_cfi_row(Arena *arena, DW_CFI_Row *row, U64 reg_count);
|
||||
|
||||
internal DW_CFI_Unwind * dw_cfi_unwind_init(Arena *arena, Arch arch, DW_CIE *cie, DW_FDE *fde, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud);
|
||||
internal B32 dw_cfi_next_row(Arena *arena, DW_CFI_Unwind *uw);
|
||||
|
||||
internal DW_CFI_Row * dw_cfi_row_from_pc(Arena *arena, Arch arch, struct DW_CIE *cie, struct DW_FDE *fde, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ctx, U64 pc);
|
||||
internal MachineOpResult dw_compute_cfa(Arch arch, DW_CFI_Row *row, void *reg_block, MachineOp_MemRead *mem_read_func, void *mem_read_ud, U64 *cfa_out);
|
||||
internal MachineOpResult dw_cfi_apply_register_rules(Arch arch, U64 cfa, DW_CFI_Row *row, void *reg_block, MachineOp_MemRead *mem_read_func, void *mem_read_ud);
|
||||
|
||||
#endif // DWARF_UNWIND_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,275 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef RDI_FROM_DWARF_H
|
||||
#define RDI_FROM_DWARF_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ Conversion
|
||||
|
||||
typedef struct D2R_ConvertParams
|
||||
{
|
||||
String8 dbg_name;
|
||||
String8 dbg_data;
|
||||
String8 exe_name;
|
||||
String8 exe_data;
|
||||
ExecutableImageKind exe_kind;
|
||||
RDIM_SubsetFlags subset_flags;
|
||||
B32 deterministic;
|
||||
B32 is_parse_relaxed;
|
||||
} D2R_ConvertParams;
|
||||
|
||||
#define D2R_UNIT_CHUNK_CAP 512
|
||||
#define D2R_UDT_CHUNK_CAP 1024
|
||||
#define D2R_TYPE_CHUNK_CAP 1024
|
||||
#define D2R_SRC_FILE_CAP 1024
|
||||
#define D2R_LINE_TABLE_CAP 1024
|
||||
#define D2R_GVAR_CHUNK_CAP 1024
|
||||
#define D2R_TVAR_CHUNK_CAP 1024
|
||||
#define D2R_LOCATIONS_CAP 4096
|
||||
#define D2R_PROC_CHUNK_CAP 4096
|
||||
#define D2R_SCOPE_CHUNK_CAP 4096
|
||||
#define D2R_INLINE_SITE_CHUNK_CAP 4096
|
||||
|
||||
typedef struct D2R_Shared
|
||||
{
|
||||
RDIM_TopLevelInfo top_level_info;
|
||||
RDIM_BinarySectionList binary_sections;
|
||||
RDIM_UnitChunkList units;
|
||||
RDIM_UDTChunkList udts;
|
||||
RDIM_TypeChunkList types;
|
||||
RDIM_SrcFileChunkList src_files;
|
||||
RDIM_LineTableChunkList line_tables;
|
||||
} D2R_Shared;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Value Types
|
||||
|
||||
typedef enum
|
||||
{
|
||||
D2R_ArithmeticType_Null,
|
||||
D2R_ArithmeticType_Signed,
|
||||
D2R_ArithmeticType_Unsigned,
|
||||
D2R_ArithmeticType_Float,
|
||||
} D2R_ArithmeticType;
|
||||
|
||||
#define D2R_ValueType_Signed_XList \
|
||||
X(S8, Signed, 1 ) \
|
||||
X(S16, Signed, 2 ) \
|
||||
X(S32, Signed, 4 ) \
|
||||
X(S64, Signed, 8 ) \
|
||||
X(S128, Signed, 16) \
|
||||
X(S256, Signed, 32) \
|
||||
X(S512, Signed, 64)
|
||||
|
||||
#define D2R_ValueType_Unsigned_XList \
|
||||
X(U8, Unsigned, 1 ) \
|
||||
X(U16, Unsigned, 2 ) \
|
||||
X(U32, Unsigned, 4 ) \
|
||||
X(U64, Unsigned, 8 ) \
|
||||
X(U128, Unsigned, 16) \
|
||||
X(U256, Unsigned, 32) \
|
||||
X(U512, Unsigned, 64)
|
||||
|
||||
#define D2R_ValueType_Float_XList \
|
||||
X(F16, Float, 2 ) \
|
||||
X(F32, Float, 4 ) \
|
||||
X(F48, Float, 6 ) \
|
||||
X(F64, Float, 8 ) \
|
||||
X(F80, Float, 10) \
|
||||
X(F96, Float, 12) \
|
||||
X(F128, Float, 16)
|
||||
|
||||
#define D2R_ValueType_XList \
|
||||
X(Generic, Null, 0 ) \
|
||||
X(ImplicitValue, Null, 0 ) \
|
||||
X(Address, Unsigned, 0 ) \
|
||||
D2R_ValueType_Signed_XList \
|
||||
D2R_ValueType_Unsigned_XList \
|
||||
D2R_ValueType_Float_XList
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define X(t, ...) D2R_ValueType_##t,
|
||||
D2R_ValueType_XList
|
||||
#undef X
|
||||
D2R_ValueType_Bool = D2R_ValueType_S8,
|
||||
} D2R_ValueType;
|
||||
|
||||
typedef struct D2R_ValueTypeNode
|
||||
{
|
||||
D2R_ValueType type;
|
||||
struct D2R_ValueTypeNode *next;
|
||||
} D2R_ValueTypeNode;
|
||||
|
||||
typedef struct D2R_ValueTypeStack
|
||||
{
|
||||
U64 count;
|
||||
D2R_ValueTypeNode *top;
|
||||
D2R_ValueTypeNode *free_list;
|
||||
} D2R_ValueTypeStack;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Type Table
|
||||
|
||||
typedef struct D2R_TypeTable
|
||||
{
|
||||
HashTable *ht;
|
||||
RDIM_TypeChunkList *types;
|
||||
U64 type_chunk_cap;
|
||||
RDIM_Type **builtin_types;
|
||||
} D2R_TypeTable;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Tag Iterator
|
||||
|
||||
typedef DW_TagSpare D2R_TagFlags;
|
||||
enum
|
||||
{
|
||||
D2R_TagFlags_TypeConverted = (1 << 0),
|
||||
D2R_TagFlags_UdtConverted = (1 << 1),
|
||||
};
|
||||
|
||||
typedef struct D2R_TagFrame
|
||||
{
|
||||
DW_TagNode *node;
|
||||
RDIM_Scope *scope;
|
||||
struct D2R_TagFrame *next;
|
||||
} D2R_TagFrame;
|
||||
|
||||
typedef struct D2R_TagIterator
|
||||
{
|
||||
D2R_TagFrame *free_list;
|
||||
D2R_TagFrame *stack;
|
||||
DW_TagNode *null_tag_node;
|
||||
DW_TagNode *tag_node;
|
||||
B32 visit_children;
|
||||
} D2R_TagIterator;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Line Table Conversion
|
||||
|
||||
typedef struct D2R_SrcFileLookup
|
||||
{
|
||||
DW_LineFile *file;
|
||||
DW_LineVM *vm;
|
||||
RDIM_SrcFile *src_file;
|
||||
} D2R_SrcFileLookup;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Contrib Map
|
||||
|
||||
typedef struct D2R_CompUnitContribMap
|
||||
{
|
||||
U64 count;
|
||||
U64 *info_off_arr;
|
||||
RDIM_Rng1U64ChunkList *voff_range_arr;
|
||||
} D2R_CompUnitContribMap;
|
||||
|
||||
////////////////////////////////
|
||||
//~ RDIM Bytecode Extensions
|
||||
|
||||
internal B32 rdim_is_eval_bytecode_static (RDIM_EvalBytecode bc);
|
||||
internal B32 rdim_static_eval_bytecode_to_voff(RDIM_EvalBytecode bc, U64 image_base, U64 *voff_out);
|
||||
|
||||
////////////////////////////////
|
||||
//~ DWARF -> RDI Enums
|
||||
|
||||
internal RDI_Language d2r_rdi_language_from_dw_language(DW_Language v);
|
||||
internal RDI_RegCodeX64 d2r_rdi_reg_code_from_dw_reg_x64 (DW_RegX64 v);
|
||||
internal RDI_RegCode d2r_rdi_reg_code_from_dw_reg (Arch arch, DW_Reg v);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Value Type
|
||||
|
||||
internal D2R_ValueTypeNode * d2r_value_type_stack_push(Arena *arena, D2R_ValueTypeStack *stack, D2R_ValueType type);
|
||||
internal D2R_ValueType d2r_value_type_stack_pop (D2R_ValueTypeStack *stack);
|
||||
internal D2R_ValueType d2r_value_type_stack_peek(D2R_ValueTypeStack *stack);
|
||||
|
||||
internal D2R_ValueType d2r_signed_value_type_from_bit_size (U64 bit_size);
|
||||
internal D2R_ValueType d2r_unsigned_value_type_from_bit_size(U64 bit_size);
|
||||
internal D2R_ValueType d2r_float_type_from_bit_size (U64 bit_size);
|
||||
|
||||
internal U64 d2r_size_from_value_type(U64 addr_size, D2R_ValueType value_type);
|
||||
internal D2R_ArithmeticType d2r_arithmetic_type_from_value_type(D2R_ValueType v);
|
||||
internal D2R_ArithmeticType d2r_arithmetic_type_from_value_type(D2R_ValueType v);
|
||||
internal B32 d2r_is_value_type_signed (D2R_ValueType v);
|
||||
internal B32 d2r_is_value_type_integral(D2R_ValueType v);
|
||||
internal B32 d2r_is_value_type_unsigned(D2R_ValueType v);
|
||||
internal B32 d2r_is_value_type_float (D2R_ValueType v);
|
||||
|
||||
internal D2R_ValueType d2r_pick_common_value_type(D2R_ValueType lhs, D2R_ValueType rhs);
|
||||
internal RDI_EvalTypeGroup d2r_value_type_to_rdi(D2R_ValueType v);
|
||||
|
||||
internal D2R_ValueType d2r_apply_usual_arithmetic_conversions(Arena *arena, D2R_ValueType lhs, D2R_ValueType rhs, RDIM_EvalBytecode *bc);
|
||||
internal void d2r_push_arithmetic_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op);
|
||||
internal void d2r_push_relational_op(Arena *arena, D2R_ValueTypeStack *stack, RDIM_EvalBytecode *bc, RDI_EvalOp op);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Expression Conversion
|
||||
|
||||
internal RDIM_EvalBytecode d2r_bytecode_from_expression(Arena *arena, DW_Raw *input, U64 image_base, Arch arch, DW_ListUnit *addr_lu, String8 expr, DW_CompUnit *cu, D2R_ValueType *result_type_out);
|
||||
internal RDIM_Location d2r_transpile_expression (Arena *arena, DW_Raw *input, U64 image_base, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr);
|
||||
internal RDIM_Location d2r_location_from_attrib (Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind);
|
||||
internal RDIM_LocationCaseList d2r_locset_from_attrib (Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *curr_scope, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind);
|
||||
internal RDIM_LocationCaseList d2r_var_locset_from_tag (Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *curr_scope, DW_Raw *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Type Table
|
||||
|
||||
internal RDIM_Type * d2r_create_type (Arena *arena, D2R_TypeTable *type_table);
|
||||
internal RDIM_Type * d2r_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off);
|
||||
internal RDIM_Type * d2r_type_from_offset(D2R_TypeTable *type_table, U64 info_off);
|
||||
internal RDIM_Type * d2r_type_from_attrib(D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Tag Iterator
|
||||
|
||||
internal D2R_TagIterator * d2r_tag_iterator_init(Arena *arena, DW_TagNode *root);
|
||||
internal void d2r_tag_iterator_next(Arena *arena, D2R_TagIterator *iter);
|
||||
internal void d2r_tag_iterator_skip_children (D2R_TagIterator *iter);
|
||||
internal DW_TagNode * d2r_tag_iterator_parent_tag_node(D2R_TagIterator *iter);
|
||||
internal DW_Tag d2r_tag_iterator_parent_tag (D2R_TagIterator *iter);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Type Conversion
|
||||
|
||||
internal void d2r_flag_converted_tag(DW_TagNode *tag_node);
|
||||
internal B8 d2r_is_tag_converted (DW_TagNode *tag_node);
|
||||
internal RDIM_Type * d2r_find_or_convert_type(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, Arch arch, DW_Tag tag, DW_AttribKind kind);
|
||||
internal void d2r_convert_types(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, Arch arch, DW_TagNode *root);
|
||||
|
||||
////////////////////////////////
|
||||
//~ UDT Conversion
|
||||
|
||||
internal B8 d2r_is_udt_tag_converted (DW_TagNode *tag_node);
|
||||
internal void d2r_flag_converted_udt_tag(DW_TagNode *tag_node);
|
||||
internal void d2r_inline_anonymous_udt_member(Arena *arena, RDIM_UDT *top_udt, U64 base_off, RDIM_UDT *udt);
|
||||
internal void d2r_convert_udts(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, DW_TagNode *root);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Symbol Conversion
|
||||
|
||||
internal RDIM_Scope * d2r_push_scope (Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D2R_TagFrame *tag_stack, Rng1U64List ranges);
|
||||
internal RDIM_Type ** d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_TagNode *cur_node, U64 *param_count_out);
|
||||
internal Rng1U64List d2r_range_list_from_tag(Arena *arena, DW_Raw *input, DW_CompUnit *cu, U64 image_base, DW_Tag tag);
|
||||
internal void d2r_convert_symbols(Arena *arena, D2R_TypeTable *type_table, DW_Raw *input, DW_CompUnit *cu, DW_Language cu_lang, U64 image_base, Arch arch, DW_TagNode *root, RDIM_Unit *unit_rdi);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Line Table Conversion
|
||||
|
||||
internal U64 d2r_hash_line_file(String8 dir_path, DW_LineFile *file);
|
||||
internal void d2r_sort_ptrs(void **ptrs, U64 count, int (* is_before)(void *a, void *b));
|
||||
|
||||
////////////////////////////////
|
||||
//~ Contrib Map
|
||||
|
||||
internal D2R_CompUnitContribMap d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Raw *input, U64 image_base);
|
||||
internal RDIM_Rng1U64ChunkList d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Entry Point
|
||||
|
||||
internal RDIM_BakeParams d2r_convert(Arena *arena, D2R_ConvertParams *params);
|
||||
|
||||
#endif // RDI_FROM_DWARF_H
|
||||
+174
-121
@@ -1,121 +1,174 @@
|
||||
version(2);
|
||||
project_name = "The RAD Debugger";
|
||||
|
||||
indent_width = "2";
|
||||
default_tab_width = "2";
|
||||
|
||||
patterns =
|
||||
{
|
||||
"*.c",
|
||||
"*.cpp",
|
||||
"*.h",
|
||||
"*.inc",
|
||||
"*.hpp",
|
||||
"*.bat",
|
||||
"*.sh",
|
||||
"*.4coder",
|
||||
"*.glsl",
|
||||
"*.bfs",
|
||||
"*.html",
|
||||
"*.txt",
|
||||
"*.md",
|
||||
"*.mdesk",
|
||||
"*.asm",
|
||||
};
|
||||
|
||||
blacklist_patterns =
|
||||
{
|
||||
".*",
|
||||
"metagen_base_*",
|
||||
"metagen_os_*",
|
||||
};
|
||||
|
||||
paths =
|
||||
{
|
||||
{ .path = ".", .recursive = false, .relative = true, },
|
||||
{ .path = "src", .recursive = true , .relative = true, },
|
||||
{ .path = "local", .recursive = true , .relative = true, },
|
||||
};
|
||||
|
||||
load_paths =
|
||||
{
|
||||
.win = paths,
|
||||
.linux = paths,
|
||||
};
|
||||
|
||||
commands =
|
||||
{
|
||||
//- rjf: [raddbg]
|
||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.f1 = { .win = "raddbg_stable --ipc kill_all && build torture && build ryan_scratch && pushd build && torture eval2/* && ryan_scratch", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: [raddbg wsl]
|
||||
// .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: [scratch]
|
||||
// .f2 = { .win = "pushd build && for /l %i in (1, 1, 3) do (radbin --rdi raddbg.exe --out:raddbg_%i.rdi && radbin --dump raddbg_%i.rdi --out:raddbg_%i.dump)", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.f2 = { .win = "pushd build && radbin --rdi raddbg.exe --thread_count=1 --out:1.rdi && radbin --rdi raddbg.exe --thread_count=8 --out:8.rdi && radbin --dump 1.rdi --out:1.dump && radbin --dump 8.rdi --out:8.dump)", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: [textperf]
|
||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build no_meta telemetry textperf && raddbg_stable --ipc bring_to_front && raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: [tester]
|
||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build no_meta tester", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: [radbin]
|
||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: running target
|
||||
.f3 = { .win = "raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f3 = { .win = "build torture", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f3 = { .win = "wsl ./build/raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f3 = { .win = "pushd build && raddbg --user:dev.raddbg_user && popd", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f3 = { .win = "C:/devel/raddebugger/build/raddbg.exe --capture --user:C:/devel/raddebugger/build/local_dev.raddbg_user --project:C:/devel/raddebugger/build/local_dev.raddbg_project", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f3 = { .win = "wsl_launch /mnt/c/devel/raddebugger/build/raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: local target builds
|
||||
.build_raddbg = { .win = "build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_rdi_from_pdb = { .win = "build rdi_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_rdi_from_dwarf = { .win = "build rdi_from_dwarf", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_rdi_dump = { .win = "build rdi_dump", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_rdi_breakpad_from_pdb = { .win = "build rdi_breakpad_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_ryan_scratch = { .win = "build ryan_scratch", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_mule_main = { .win = "build mule_main", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_mule_module = { .win = "build mule_module", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_mule_hotload = { .win = "build mule_hotload", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_mule_peb_trample = { .win = "build mule_peb_trample", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: wsl target builds
|
||||
.build_raddbg_wsl = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_rdi_from_pdb_wsl = { .win = "wsl ./build.sh rdi_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_rdi_from_dwarf_wsl = { .win = "wsl ./build.sh rdi_from_dwarf", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_rdi_dump_wsl = { .win = "wsl ./build.sh rdi_dump", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_rdi_breakpad_from_pdb_wsl = { .win = "wsl ./build.sh rdi_breakpad_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_ryan_scratch_wsl = { .win = "wsl ./build.sh ryan_scratch", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_mule_main_wsl = { .win = "wsl ./build.sh mule_main", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_mule_module_wsl = { .win = "wsl ./build.sh mule_module", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_mule_hotload_wsl = { .win = "wsl ./build.sh mule_hotload", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.build_mule_peb_trample_wsl = { .win = "wsl ./build.sh mule_peb_trample", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: local target runs
|
||||
.run_raddbg = { .win = "pushd build && raddbg.exe && popd", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
};
|
||||
|
||||
fkey_command =
|
||||
{
|
||||
.F1 = "f1",
|
||||
.F2 = "f2",
|
||||
.F3 = "f3",
|
||||
};
|
||||
|
||||
fkey_command_override =
|
||||
{
|
||||
.rjf =
|
||||
{
|
||||
.F1 = "f1",
|
||||
.F2 = "f2",
|
||||
.F3 = "f3",
|
||||
},
|
||||
};
|
||||
version(2);
|
||||
project_name = "The RAD Debugger";
|
||||
|
||||
indent_width = "2";
|
||||
default_tab_width = "2";
|
||||
|
||||
patterns =
|
||||
{
|
||||
"*.c",
|
||||
"*.cpp",
|
||||
"*.h",
|
||||
"*.inc",
|
||||
"*.hpp",
|
||||
"*.bat",
|
||||
"*.sh",
|
||||
"*.4coder",
|
||||
"*.glsl",
|
||||
"*.bfs",
|
||||
"*.html",
|
||||
"*.txt",
|
||||
"*.md",
|
||||
"*.mdesk",
|
||||
"*.asm",
|
||||
};
|
||||
|
||||
blacklist_patterns =
|
||||
{
|
||||
".*",
|
||||
"metagen_base_*",
|
||||
"metagen_os_*",
|
||||
};
|
||||
|
||||
paths =
|
||||
{
|
||||
{ .path = ".", .recursive = false, .relative = true, },
|
||||
{ .path = "src", .recursive = true , .relative = true, },
|
||||
{ .path = "local", .recursive = true , .relative = true, },
|
||||
};
|
||||
|
||||
load_paths =
|
||||
{
|
||||
.win = paths,
|
||||
.linux = paths,
|
||||
};
|
||||
|
||||
commands =
|
||||
{
|
||||
.rjf_f1 =
|
||||
{
|
||||
.win = "build raddbg telemetry",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.rjf_f2 =
|
||||
{
|
||||
.win = "build mule_peb_trample",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.rjf_f3 =
|
||||
{
|
||||
.win = "pushd build && raddbg.exe --user:local_dev.raddbg_user --profile:local_dev.raddbg_profile && popd",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.rjf_f4 =
|
||||
{
|
||||
.win = "build rdi_from_pdb release telemetry && pushd build && rdi_from_pdb.exe --exe:UnrealEditorFortnite.exe --pdb:UnrealEditorFortnite.pdb --out:UnrealEditorFortnite.rdi --capture && popd",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.rjf_f5 =
|
||||
{
|
||||
.win = "pushd build && rdi_from_pdb.exe --exe:raddbg.exe --pdb:raddbg.pdb --out:raddbg.rdi --capture && popd",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.build_raddbg =
|
||||
{
|
||||
.win = "build raddbg",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.build_raddbg_release_telemetry =
|
||||
{
|
||||
.win = "build raddbg release telemetry",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.build_rdi_from_pdb =
|
||||
{
|
||||
.win = "build rdi_from_pdb",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.build_rdi_dump =
|
||||
{
|
||||
.win = "build rdi_dump",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.build_mule_main =
|
||||
{
|
||||
.win = "build mule_main",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.build_ryan_scratch =
|
||||
{
|
||||
.win = "build ryan_scratch",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
.run_raddbg =
|
||||
{
|
||||
.win = "pushd build && raddbg.exe && popd",
|
||||
.linux = "",
|
||||
.out = "*compilation*",
|
||||
.footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.cursor_at_end = false,
|
||||
},
|
||||
};
|
||||
|
||||
fkey_command =
|
||||
{
|
||||
.F1 = "build_raddbg",
|
||||
.F3 = "run_raddbg",
|
||||
};
|
||||
|
||||
fkey_command_override =
|
||||
{
|
||||
.rjf =
|
||||
{
|
||||
.F1 = "rjf_f1",
|
||||
.F2 = "rjf_f2",
|
||||
.F3 = "rjf_f3",
|
||||
.F4 = "rjf_f4",
|
||||
.F5 = "rjf_f5",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
// raddbg 0.9.28 project file
|
||||
|
||||
name: "The RAD Debugger"
|
||||
target:
|
||||
{
|
||||
executable: "build/raddbg.exe"
|
||||
working_directory: "../raddebugger"
|
||||
arguments: "--user:C:/devel/raddebugger/build/raddbg_test.user"
|
||||
debug_subprocesses: 0
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/raddbg.exe"
|
||||
working_directory: "../raddebugger"
|
||||
arguments: "--user:test123"
|
||||
debug_subprocesses: 0
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/mule_main.exe"
|
||||
working_directory: build
|
||||
output_label: output_a
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/torture.exe"
|
||||
working_directory: build
|
||||
arguments: "eval2/*"
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/metagen.exe"
|
||||
working_directory: build
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/radbin.exe"
|
||||
working_directory: build
|
||||
arguments: "--rdi raddbg"
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/release_version/raddbg.exe"
|
||||
working_directory: "build/release_version"
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/radbin.exe"
|
||||
working_directory: build
|
||||
arguments: "--rdi fat.so --capture"
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/radbin.exe"
|
||||
working_directory: build
|
||||
arguments: "--rdi fat.debug --capture"
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "build/radbin.exe"
|
||||
working_directory: build
|
||||
arguments: "--rdi raddbg"
|
||||
}
|
||||
watch_pin: expression: tag_hash_slots
|
||||
target:
|
||||
{
|
||||
executable: "build/ryan_scratch.exe"
|
||||
working_directory: build
|
||||
enabled: 1
|
||||
}
|
||||
target:
|
||||
{
|
||||
executable: "../../Program Files/SuperTux/bin/supertux2.exe"
|
||||
working_directory: "../../Program Files/SuperTux/bin"
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
set TARGET_VALUES=radlink radbin mule_main mule_module torture
|
||||
set CC_VALUES=msvc clang
|
||||
set MODE_VALUES=debug release
|
||||
set RAW_ARGS=%*
|
||||
set TORTURE_ARGS=
|
||||
set RUN_TORTURE=1
|
||||
|
||||
:: Make test data directories
|
||||
if not exist local mkdir local
|
||||
pushd local
|
||||
if not exist test_data mkdir test_data
|
||||
popd
|
||||
|
||||
:: Check if we need new test data version
|
||||
pushd local
|
||||
set need_test_data=0
|
||||
curl -sfL -o test_data_version_latest.txt https://data.raddbg.com/public/raddbg_test_data_version.txt || exit /b 1
|
||||
if exist "test_data_version_current.txt" (
|
||||
fc /b "test_data_version_latest.txt" "test_data_version_current.txt" > nul
|
||||
) else (
|
||||
echo Test data not present.
|
||||
set need_test_data=1
|
||||
)
|
||||
if errorlevel 1 (
|
||||
echo Test data out-of-date.
|
||||
set need_test_data=1
|
||||
)
|
||||
popd
|
||||
|
||||
:: Download test data if stale
|
||||
if "%need_test_data%"=="1" (
|
||||
echo Downloading test data...
|
||||
pushd local
|
||||
pushd test_data
|
||||
curl -sfL -o test_data.zip https://data.raddbg.com/public/raddbg_test_data.zip || exit /b 1
|
||||
tar -xf test_data.zip || exit /b 1
|
||||
del test_data.zip
|
||||
popd
|
||||
copy /y test_data_version_latest.txt test_data_version_current.txt
|
||||
popd
|
||||
)
|
||||
popd
|
||||
|
||||
:parse_args
|
||||
if "%~1" == "" goto parse_done
|
||||
if "%~1" == "--" goto parse_torture_args
|
||||
if /i "%~1" == "msvc" set CC_VALUES=msvc && shift /1 && goto parse_args
|
||||
if /i "%~1" == "clang" set CC_VALUES=clang && shift /1 && goto parse_args
|
||||
if /i "%~1" == "debug" set MODE_VALUES=debug && shift /1 && goto parse_args
|
||||
if /i "%~1" == "release" set MODE_VALUES=release && shift /1 && goto parse_args
|
||||
if /i "%~1" == "no_torture" set RUN_TORTURE=0 && shift /1 && goto parse_args
|
||||
echo usage: %~nx0 [msvc^|clang] [debug^|release] [no_torture] [-- torture-args]
|
||||
exit /b 1
|
||||
|
||||
:parse_torture_args
|
||||
shift /1
|
||||
if "%~1" == "" set "TORTURE_ARGS=" && goto parse_done
|
||||
set "TORTURE_ARGS=%RAW_ARGS%"
|
||||
if "%TORTURE_ARGS:~0,3%" == "-- " set "TORTURE_ARGS=%TORTURE_ARGS:~3%" && goto parse_done
|
||||
set "TORTURE_ARGS=%TORTURE_ARGS:* -- =%"
|
||||
|
||||
:parse_done
|
||||
if not defined TORTURE_ARGS set "TORTURE_ARGS=*
|
||||
|
||||
for %%m in (%MODE_VALUES%) do for %%c in (%CC_VALUES%) do (
|
||||
setlocal
|
||||
|
||||
:: nuke artifacts from last run
|
||||
if exist build/torture_artifacts rmdir /s /q build\torture_artifacts
|
||||
if exist build\metagen.exe del /q build\metagen.exe
|
||||
for %%t in (%TARGET_VALUES%) do (
|
||||
if exist build\%%t.exe del /q build\%%t.exe
|
||||
if exist build\%%t.pdb del /q build\%%t.pdb
|
||||
)
|
||||
|
||||
if "%%c" == "clang" (
|
||||
:: Prefer standalone LLVM clang over Visual Studio clang so ASAN runtime files match the compiler.
|
||||
set "clang_path="
|
||||
for /f "tokens=*" %%i in ('where clang') do (
|
||||
set "candidate_clang_path=%%~dpi"
|
||||
if "!candidate_clang_path:Microsoft Visual Studio=!"=="!candidate_clang_path!" (
|
||||
if not defined clang_path set "clang_path=!candidate_clang_path!"
|
||||
)
|
||||
)
|
||||
if not defined clang_path (
|
||||
echo ERROR: standalone LLVM clang not found in PATH
|
||||
exit /b 1
|
||||
)
|
||||
set PATH=!clang_path!;!PATH!
|
||||
for /f "tokens=3 delims=. " %%v in ('clang -v 2^>^&1 ^| findstr version') do set clang_version=%%v
|
||||
if not defined clang_version (
|
||||
echo ERROR: failed to detect clang version
|
||||
exit /b 1
|
||||
)
|
||||
set PATH=!clang_path!..\lib\clang\!clang_version!\lib\windows;!PATH!
|
||||
)
|
||||
|
||||
:: TODO: unblock asan
|
||||
call build.bat meta %%c %%m raddbg raddbg_non_graphical || (endlocal exit /b 1)
|
||||
|
||||
:: clang does not compile with asan in release mode because it runs out of memory
|
||||
if "%%c" equ "clang" (
|
||||
if /i "%%m" equ "release" (
|
||||
call build.bat no_meta %%c %%m !TARGET_VALUES! || (endlocal exit /b 1)
|
||||
) else (
|
||||
call build.bat asan no_meta %%c %%m !TARGET_VALUES! || (endlocal exit /b 1)
|
||||
)
|
||||
) else (
|
||||
call build.bat asan no_meta %%c %%m !TARGET_VALUES! || (endlocal exit /b 1)
|
||||
)
|
||||
|
||||
if "%RUN_TORTURE%" equ "1" (
|
||||
pushd build
|
||||
torture %TORTURE_ARGS%
|
||||
popd
|
||||
if errorlevel 1 (
|
||||
endlocal
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
endlocal
|
||||
)
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
target_values=(raddbg raddbg_non_graphical radlink radbin torture)
|
||||
cc_values=(clang gcc)
|
||||
mode_values=(debug release)
|
||||
run_torture=1
|
||||
torture_args=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--)
|
||||
shift
|
||||
torture_args=("$@")
|
||||
break
|
||||
;;
|
||||
clang)
|
||||
cc_values=("$1")
|
||||
shift
|
||||
;;
|
||||
gcc)
|
||||
cc_values=("$1")
|
||||
shift
|
||||
;;
|
||||
debug|release)
|
||||
mode_values=("$1")
|
||||
shift
|
||||
;;
|
||||
no_torture)
|
||||
run_torture=0
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 [clang|gcc] [debug|release] [no_torture] [-- torture-args]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "${#torture_args[@]}" == "0" ]]; then
|
||||
torture_args=("*")
|
||||
fi
|
||||
|
||||
for m in "${mode_values[@]}"; do
|
||||
for c in "${cc_values[@]}"; do
|
||||
# nuke artifacts from last run
|
||||
rm -rf build/torture_artifacts
|
||||
rm -f build/metagen
|
||||
for t in "${target_values[@]}"; do
|
||||
rm -f "build/$t"
|
||||
done
|
||||
|
||||
# build targets
|
||||
./build.sh meta "$c" "$m" "${target_values[@]}"
|
||||
|
||||
# run torture from build folder
|
||||
if [[ "$run_torture" == "1" ]]; then
|
||||
torture_compiler_args=()
|
||||
has_clang_arg=0
|
||||
has_gcc_arg=0
|
||||
for arg in "${torture_args[@]}"; do
|
||||
case "$arg" in
|
||||
-clang:*|--clang:*) has_clang_arg=1 ;;
|
||||
-gcc:*|--gcc:*) has_gcc_arg=1 ;;
|
||||
esac
|
||||
done
|
||||
if [[ "$c" == clang && "$has_clang_arg" == "0" ]]; then
|
||||
torture_compiler_args=("-clang:$(command -v "${CC:-clang}")")
|
||||
elif [[ "$c" == gcc && "$has_gcc_arg" == "0" ]]; then
|
||||
torture_compiler_args=("-gcc:$(command -v "${CC:-gcc}")")
|
||||
fi
|
||||
(cd build && ./torture "${torture_compiler_args[@]}" "${torture_args[@]}")
|
||||
fi
|
||||
|
||||
done
|
||||
done
|
||||
-174
@@ -1,174 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Abstracted Architecture Functions
|
||||
|
||||
internal ARCH_Info *
|
||||
arch_info_from_arch(Arch arch)
|
||||
{
|
||||
ARCH_Info *result = &arch_info_nil;
|
||||
switch(arch)
|
||||
{
|
||||
default:{}break;
|
||||
#if defined(X64_H)
|
||||
case Arch_x64:
|
||||
{
|
||||
local_persist U8 trap_inst_bytes[] = {0xcc};
|
||||
local_persist read_only ARCH_Info info =
|
||||
{
|
||||
.reg_block_size = sizeof(X64_RegBlock),
|
||||
.instruction_pointer_reg_code = X64_RegCode_rip,
|
||||
.stack_pointer_reg_code = X64_RegCode_rsp,
|
||||
.reg_code_count = X64_RegCode_COUNT,
|
||||
.trap_instruction = {trap_inst_bytes, sizeof(trap_inst_bytes)},
|
||||
.reg_code_rng_table = x64_reg_code_rng_table,
|
||||
.reg_code_name_table = x64_reg_code_name_table,
|
||||
.reg_code_base_table = x64_reg_code_base_table,
|
||||
.reg_code_is_vector_table = x64_reg_code_is_vector_table,
|
||||
};
|
||||
result = &info;
|
||||
}break;
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal ARCH_RegCode
|
||||
arch_reg_code_from_name(ARCH_Info *arch_info, String8 name)
|
||||
{
|
||||
ARCH_RegCode result = 0;
|
||||
for EachIndex(code, arch_info->reg_code_count)
|
||||
{
|
||||
if(str8_match(arch_info->reg_code_name_table[code], name, 0))
|
||||
{
|
||||
result = code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
arch_reg_block_read_range(ARCH_Info *arch_info, void *block, Rng1U16 range, void *dst)
|
||||
{
|
||||
B32 result = 0;
|
||||
{
|
||||
Rng1U16 legal_range = r1u16(0, arch_info->reg_block_size);
|
||||
Rng1U16 try_range = range;
|
||||
Rng1U16 read_range = intersect_1u16(legal_range, try_range);
|
||||
U64 read_size = dim_1u16(read_range);
|
||||
if(read_size != 0)
|
||||
{
|
||||
MemoryCopy(dst, (U8 *)block + read_range.min, read_size);
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
arch_reg_block_write_range(ARCH_Info *arch_info, void *block, Rng1U16 range, void *src)
|
||||
{
|
||||
B32 result = 0;
|
||||
Rng1U16 legal_range = r1u16(0, arch_info->reg_block_size);
|
||||
Rng1U16 try_range = range;
|
||||
Rng1U16 write_range = intersect_1u16(legal_range, try_range);
|
||||
U64 write_size = dim_1u16(write_range);
|
||||
if(write_size != 0)
|
||||
{
|
||||
MemoryCopy((U8 *)block + write_range.min, src, write_size);
|
||||
result = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
arch_ip_from_reg_block(ARCH_Info *arch_info, void *block)
|
||||
{
|
||||
U64 result = 0;
|
||||
ARCH_RegCode reg_code = arch_info->instruction_pointer_reg_code;
|
||||
if(reg_code < arch_info->reg_code_count)
|
||||
{
|
||||
arch_reg_block_read_range(arch_info, block, arch_info->reg_code_rng_table[reg_code], &result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
arch_sp_from_reg_block(ARCH_Info *arch_info, void *block)
|
||||
{
|
||||
U64 result = 0;
|
||||
ARCH_RegCode reg_code = arch_info->stack_pointer_reg_code;
|
||||
if(reg_code < arch_info->reg_code_count)
|
||||
{
|
||||
arch_reg_block_read_range(arch_info, block, arch_info->reg_code_rng_table[reg_code], &result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
arch_reg_block_write_ip(ARCH_Info *arch_info, void *block, U64 ip)
|
||||
{
|
||||
B32 result = 0;
|
||||
ARCH_RegCode reg_code = arch_info->instruction_pointer_reg_code;
|
||||
if(reg_code < arch_info->reg_code_count)
|
||||
{
|
||||
result = arch_reg_block_write_range(arch_info, block, arch_info->reg_code_rng_table[reg_code], &ip);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
arch_reg_block_write_sp(ARCH_Info *arch_info, void *block, U64 sp)
|
||||
{
|
||||
B32 result = 0;
|
||||
ARCH_RegCode reg_code = arch_info->stack_pointer_reg_code;
|
||||
if(reg_code < arch_info->reg_code_count)
|
||||
{
|
||||
result = arch_reg_block_write_range(arch_info, block, arch_info->reg_code_rng_table[reg_code], &sp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(RDI_H)
|
||||
internal ARCH_RegCode
|
||||
arch_reg_code_from_rdi(Arch arch, RDI_RegCode code)
|
||||
{
|
||||
ARCH_RegCode result = 0;
|
||||
{
|
||||
ARCH_Info *arch_info = arch_info_from_arch(arch);
|
||||
U8 *rdi_from_reg_code_table = arch_rdi_from_reg_code_table_from_arch(arch);
|
||||
for EachIndex(c, arch_info->reg_code_count)
|
||||
{
|
||||
if(rdi_from_reg_code_table[c] == code)
|
||||
{
|
||||
result = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DWARF_H)
|
||||
internal ARCH_RegCode
|
||||
arch_reg_code_from_dw(Arch arch, DW_RegCode code)
|
||||
{
|
||||
ARCH_RegCode result = 0;
|
||||
{
|
||||
ARCH_Info *arch_info = arch_info_from_arch(arch);
|
||||
U8 *dw_from_reg_code_table = arch_dw_from_reg_code_table_from_arch(arch);
|
||||
for EachIndex(c, arch_info->reg_code_count)
|
||||
{
|
||||
if(dw_from_reg_code_table[c] == code)
|
||||
{
|
||||
result = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef ARCH_H
|
||||
#define ARCH_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Abstraction Backend Info
|
||||
|
||||
typedef U8 ARCH_RegCode;
|
||||
|
||||
typedef struct ARCH_Info ARCH_Info;
|
||||
struct ARCH_Info
|
||||
{
|
||||
U16 reg_block_size;
|
||||
ARCH_RegCode instruction_pointer_reg_code;
|
||||
ARCH_RegCode stack_pointer_reg_code;
|
||||
U16 reg_code_count;
|
||||
String8 trap_instruction;
|
||||
Rng1U16 *reg_code_rng_table;
|
||||
String8 *reg_code_name_table;
|
||||
U8 *reg_code_base_table;
|
||||
B8 *reg_code_is_vector_table;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global read_only Rng1U16 arch_reg_code_rng_nil = {0};
|
||||
global read_only String8 arch_reg_code_name_nil = {0};
|
||||
global read_only U8 arch_reg_code_u8_nil = 0;
|
||||
global read_only B8 arch_reg_code_b8_nil = 0;
|
||||
global read_only ARCH_Info arch_info_nil = {0, 0, 0, 0, {0}, &arch_reg_code_rng_nil, &arch_reg_code_name_nil, &arch_reg_code_u8_nil, &arch_reg_code_b8_nil};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Abstracted Architecture Functions
|
||||
|
||||
internal ARCH_Info *arch_info_from_arch(Arch arch);
|
||||
internal ARCH_RegCode arch_reg_code_from_name(ARCH_Info *arch_info, String8 name);
|
||||
internal B32 arch_reg_block_read_range(ARCH_Info *arch_info, void *block, Rng1U16 range, void *dst);
|
||||
internal B32 arch_reg_block_write_range(ARCH_Info *arch_info, void *block, Rng1U16 range, void *src);
|
||||
internal U64 arch_ip_from_reg_block(ARCH_Info *arch_info, void *block);
|
||||
internal U64 arch_sp_from_reg_block(ARCH_Info *arch_info, void *block);
|
||||
internal B32 arch_reg_block_write_ip(ARCH_Info *arch_info, void *block, U64 ip);
|
||||
internal B32 arch_reg_block_write_sp(ARCH_Info *arch_info, void *block, U64 sp);
|
||||
|
||||
#if defined(RDI_H)
|
||||
internal ARCH_RegCode arch_reg_code_from_rdi(Arch arch, RDI_RegCode code);
|
||||
#endif
|
||||
#if defined(DWARF_H)
|
||||
internal ARCH_RegCode arch_reg_code_from_dw(Arch arch, DW_RegCode code);
|
||||
#endif
|
||||
|
||||
#endif // REGS_H
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#include "arch/arch.c"
|
||||
#include "arch/os/arch_os.c"
|
||||
#if defined(DWARF_H)
|
||||
# include "arch/dwarf/arch_dwarf.c"
|
||||
#endif
|
||||
#if defined(RDI_H)
|
||||
# include "arch/rdi/arch_rdi.c"
|
||||
#endif
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef ARCH_INC_H
|
||||
#define ARCH_INC_H
|
||||
|
||||
#include "arch/arch.h"
|
||||
#include "arch/os/arch_os.h"
|
||||
#if defined(DWARF_H)
|
||||
# include "arch/dwarf/arch_dwarf.h"
|
||||
#endif
|
||||
#if defined(RDI_H)
|
||||
# include "arch/rdi/arch_rdi.h"
|
||||
#endif
|
||||
|
||||
#endif // ARCH_INC_H
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal U8 *
|
||||
arch_dw_from_reg_code_table_from_arch(Arch arch)
|
||||
{
|
||||
U8 *result = &arch_reg_code_u8_nil;
|
||||
switch(arch)
|
||||
{
|
||||
default:{}break;
|
||||
#if defined(X64_H)
|
||||
case Arch_x64:
|
||||
{
|
||||
result = dw_reg_code_from_x64_table;
|
||||
}break;
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef ARCH_DWARF_H
|
||||
#define ARCH_DWARF_H
|
||||
|
||||
internal U8 *arch_dw_from_reg_code_table_from_arch(Arch arch);
|
||||
|
||||
#endif // ARCH_DWARF_H
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal B32
|
||||
arch_os_write_reg_block_from_thread_ctx(Arch arch, OperatingSystem os, void *reg_block, void *thread_ctx)
|
||||
{
|
||||
B32 result = 0;
|
||||
if(0){}
|
||||
#define Case(arch_, os_, impl) else if((arch) == arch_ && (os) == os_) do {result = impl(reg_block, thread_ctx);}while(0)
|
||||
#if defined(X64_H) && defined(WIN32_X64_H)
|
||||
Case(Arch_x64, OperatingSystem_Windows, w32_x64_write_reg_block_from_thread_ctx);
|
||||
#endif
|
||||
#undef Case
|
||||
else{}
|
||||
return result;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef ARCH_OS_H
|
||||
#define ARCH_OS_H
|
||||
|
||||
internal B32 arch_os_write_reg_block_from_thread_ctx(Arch arch, OperatingSystem os, void *reg_block, void *thread_ctx);
|
||||
|
||||
#endif // ARCH_OS_H
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal U8 *
|
||||
arch_rdi_from_reg_code_table_from_arch(Arch arch)
|
||||
{
|
||||
U8 *result = &arch_reg_code_u8_nil;
|
||||
switch(arch)
|
||||
{
|
||||
default:{}break;
|
||||
#if defined(X64_H)
|
||||
case Arch_x64:
|
||||
{
|
||||
result = rdi_reg_code_from_x64_table;
|
||||
}break;
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef ARCH_RDI_H
|
||||
#define ARCH_RDI_H
|
||||
|
||||
internal U8 *arch_rdi_from_reg_code_table_from_arch(Arch arch);
|
||||
|
||||
#endif // ARCH_RDI_H
|
||||
@@ -1,735 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Layer Initialization
|
||||
|
||||
internal void
|
||||
ac_init(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
ac_shared = push_array(arena, AC_Shared, 1);
|
||||
ac_shared->arena = arena;
|
||||
ac_shared->cache_slots_count = 256;
|
||||
ac_shared->cache_slots = push_array(arena, AC_Cache *, ac_shared->cache_slots_count);
|
||||
ac_shared->cache_stripes = stripe_array_alloc(arena);
|
||||
for EachElement(idx, ac_shared->req_batches)
|
||||
{
|
||||
ac_shared->req_batches[idx].mutex = mutex_alloc();
|
||||
ac_shared->req_batches[idx].arena = arena_alloc();
|
||||
}
|
||||
ac_shared->cancel_thread_semaphore = semaphore_alloc(0, 1, str8_zero());
|
||||
ac_shared->cancel_thread = thread_launch(ac_cancel_thread_entry_point, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal AC_Artifact
|
||||
ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U64 endt_us)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
AC_RequestBatch *req_batch = &ac_shared->req_batches[params->flags & AC_Flag_HighPriority ? 0 : 1];
|
||||
|
||||
//- rjf: create function -> cache
|
||||
AC_Cache *cache = 0;
|
||||
{
|
||||
U64 cache_hash = u64_hash_from_str8(str8_struct(¶ms->create));
|
||||
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
|
||||
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
|
||||
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
|
||||
{
|
||||
RWMutexScope(cache_stripe->rw_mutex, write_mode)
|
||||
{
|
||||
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
|
||||
{
|
||||
if(c->create == params->create)
|
||||
{
|
||||
cache = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(write_mode && cache == 0)
|
||||
{
|
||||
cache = push_array(cache_stripe->arena, AC_Cache, 1);
|
||||
SLLStackPush(ac_shared->cache_slots[cache_slot_idx], cache);
|
||||
cache->create = params->create;
|
||||
cache->destroy = params->destroy;
|
||||
cache->slots_count = Max(256, params->slots_count);
|
||||
cache->slots = push_array(cache_stripe->arena, AC_Slot, cache->slots_count);
|
||||
cache->stripes = stripe_array_alloc(cache_stripe->arena);
|
||||
cache->stripe_free_node_ptrs = push_array(cache_stripe->arena, AC_NodePtr *, cache->stripes.count);
|
||||
}
|
||||
}
|
||||
if(cache != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: unpack key
|
||||
U64 hash = u64_hash_from_str8(key);
|
||||
U64 slot_idx = hash%cache->slots_count;
|
||||
AC_Slot *slot = &cache->slots[slot_idx];
|
||||
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
|
||||
|
||||
//- rjf: unpack thread context
|
||||
B32 is_node_work_active = (ac_tctx != 0);
|
||||
|
||||
//- rjf: cache * key -> existing artifact
|
||||
B32 artifact_is_stale = 1;
|
||||
B32 got_artifact = 0;
|
||||
B32 need_request = 0;
|
||||
AC_Artifact artifact = {0};
|
||||
RWMutexScope(stripe->rw_mutex, 0)
|
||||
{
|
||||
for(AC_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(str8_match(n->key, key, 0))
|
||||
{
|
||||
ins_atomic_u64_eval_assign(&n->last_requested_gen, params->gen);
|
||||
B32 is_stale = (n->last_completed_gen != params->gen);
|
||||
if(ins_atomic_u64_eval(&n->completion_count) != 0 && (!is_stale || !(params->flags & AC_Flag_WaitForFresh)))
|
||||
{
|
||||
got_artifact = 1;
|
||||
artifact_is_stale = is_stale;
|
||||
artifact = n->val;
|
||||
access_touch(access, &n->access_pt, stripe->cv);
|
||||
ins_atomic_u64_eval_assign(&n->last_touched_ac_request_gen, ins_atomic_u64_eval(&ac_shared->request_gen));
|
||||
}
|
||||
if(is_stale)
|
||||
{
|
||||
B32 got_task = (ins_atomic_u64_eval_cond_assign(&n->working_count, 1, 0) == 0);
|
||||
need_request = got_task;
|
||||
if(is_node_work_active)
|
||||
{
|
||||
ins_atomic_u32_eval_assign(&n->other_nodes_depend_on_me, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: didn't get artifact we want? -> fall back to slow path
|
||||
if(!got_artifact || need_request)
|
||||
{
|
||||
RWMutexScope(stripe->rw_mutex, 1) for(;;)
|
||||
{
|
||||
B32 out_of_time = (now_time_us() >= endt_us);
|
||||
|
||||
// rjf: find node in cache
|
||||
AC_Node *node = 0;
|
||||
for(AC_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(str8_match(n->key, key, 0))
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: no node? -> create
|
||||
if(node == 0)
|
||||
{
|
||||
need_request = 1;
|
||||
node = stripe->free;
|
||||
if(node)
|
||||
{
|
||||
stripe->free = node->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = push_array_no_zero(stripe->arena, AC_Node, 1);
|
||||
}
|
||||
MemoryZeroStruct(node);
|
||||
DLLPushBack(slot->first, slot->last, node);
|
||||
// TODO(rjf): string allocator for keys
|
||||
node->key = str8_copy(stripe->arena, key);
|
||||
node->working_count = 1;
|
||||
node->evict_threshold_us = params->evict_threshold_us;
|
||||
if(is_node_work_active)
|
||||
{
|
||||
node->other_nodes_depend_on_me = 1;
|
||||
}
|
||||
}
|
||||
node->access_pt.last_time_touched_us = now_time_us();
|
||||
node->access_pt.last_update_idx_touched = update_tick_idx();
|
||||
|
||||
// rjf: request
|
||||
if(need_request)
|
||||
{
|
||||
need_request = 0;
|
||||
MutexScope(req_batch->mutex)
|
||||
{
|
||||
AC_RequestNode *n = push_array(req_batch->arena, AC_RequestNode, 1);
|
||||
if(params->flags & AC_Flag_Wide)
|
||||
{
|
||||
SLLQueuePush(req_batch->first_wide, req_batch->last_wide, n);
|
||||
req_batch->wide_count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
SLLQueuePush(req_batch->first_thin, req_batch->last_thin, n);
|
||||
req_batch->thin_count += 1;
|
||||
}
|
||||
n->v.key = str8_copy(req_batch->arena, key);
|
||||
n->v.gen = params->gen;
|
||||
n->v.cancel_signal = &node->cancelled;
|
||||
n->v.create = params->create;
|
||||
}
|
||||
cond_var_broadcast(async_tick_start_cond_var);
|
||||
ins_atomic_u32_eval_assign(&async_loop_again, 1);
|
||||
if(params->flags & AC_Flag_HighPriority)
|
||||
{
|
||||
ins_atomic_u32_eval_assign(&async_loop_again_high_priority, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: get value from node, if possible
|
||||
if(!got_artifact && ins_atomic_u64_eval(&node->completion_count) != 0 && ((node->last_completed_gen == params->gen) || !(params->flags & AC_Flag_WaitForFresh) || out_of_time))
|
||||
{
|
||||
got_artifact = 1;
|
||||
artifact_is_stale = (node->last_completed_gen != params->gen);
|
||||
artifact = node->val;
|
||||
access_touch(access, &node->access_pt, stripe->cv);
|
||||
ins_atomic_u64_eval_assign(&node->last_touched_ac_request_gen, ins_atomic_u64_eval(&ac_shared->request_gen));
|
||||
}
|
||||
|
||||
// rjf: abort if needed
|
||||
if(out_of_time || got_artifact || is_async_thread)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// rjf: wait for results
|
||||
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, endt_us);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: report staleness
|
||||
if(params->stale_out)
|
||||
{
|
||||
params->stale_out[0] = artifact_is_stale;
|
||||
}
|
||||
|
||||
ProfEnd();
|
||||
return artifact;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Tick
|
||||
|
||||
internal void
|
||||
ac_async_tick(void)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: enable cancellation scanning
|
||||
//
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
semaphore_drop(ac_shared->cancel_thread_semaphore);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: do eviction pass across all caches
|
||||
//
|
||||
for EachIndex(cache_slot_idx, ac_shared->cache_slots_count)
|
||||
{
|
||||
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
|
||||
RWMutexScope(cache_stripe->rw_mutex, 0)
|
||||
{
|
||||
for EachNode(cache, AC_Cache, ac_shared->cache_slots[cache_slot_idx])
|
||||
{
|
||||
Rng1U64 slot_range = lane_range(cache->slots_count);
|
||||
for EachInRange(slot_idx, slot_range)
|
||||
{
|
||||
AC_Slot *slot = &cache->slots[slot_idx];
|
||||
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
|
||||
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
|
||||
{
|
||||
B32 slot_has_work = 0;
|
||||
RWMutexScope(stripe->rw_mutex, write_mode)
|
||||
{
|
||||
for(AC_Node *n = slot->first, *next = 0; n != 0; n = next)
|
||||
{
|
||||
next = n->next;
|
||||
if(access_pt_is_expired(&n->access_pt, .time = n->evict_threshold_us) &&
|
||||
ins_atomic_u64_eval(&n->working_count) == 0 &&
|
||||
(ins_atomic_u32_eval(&n->other_nodes_depend_on_me) == 0 || ac_shared->request_gen > n->last_touched_ac_request_gen))
|
||||
{
|
||||
slot_has_work = 1;
|
||||
if(!write_mode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
DLLRemove(slot->first, slot->last, n);
|
||||
n->next = (AC_Node *)stripe->free;
|
||||
stripe->free = n;
|
||||
if(cache->destroy)
|
||||
{
|
||||
cache->destroy(n->val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!slot_has_work)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: gather requests
|
||||
//
|
||||
typedef struct RequestBatchTask RequestBatchTask;
|
||||
struct RequestBatchTask
|
||||
{
|
||||
AC_Request *wide;
|
||||
U64 wide_count;
|
||||
AC_Request *thin;
|
||||
U64 thin_count;
|
||||
};
|
||||
RequestBatchTask *tasks = 0;
|
||||
U64 tasks_count = 0;
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
tasks_count = 2;
|
||||
tasks = push_array(scratch.arena, RequestBatchTask, tasks_count);
|
||||
for EachElement(task_idx, ac_shared->req_batches)
|
||||
{
|
||||
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
|
||||
MutexScope(batch->mutex)
|
||||
{
|
||||
tasks[task_idx].wide_count = batch->wide_count;
|
||||
tasks[task_idx].thin_count = batch->thin_count;
|
||||
tasks[task_idx].wide = push_array(scratch.arena, AC_Request, tasks[task_idx].wide_count);
|
||||
tasks[task_idx].thin = push_array(scratch.arena, AC_Request, tasks[task_idx].thin_count);
|
||||
{
|
||||
U64 idx = 0;
|
||||
for EachNode(n, AC_RequestNode, batch->first_wide)
|
||||
{
|
||||
MemoryCopyStruct(&tasks[task_idx].wide[idx], &n->v);
|
||||
tasks[task_idx].wide[idx].key = str8_copy(scratch.arena, tasks[task_idx].wide[idx].key);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
{
|
||||
U64 idx = 0;
|
||||
for EachNode(n, AC_RequestNode, batch->first_thin)
|
||||
{
|
||||
MemoryCopyStruct(&tasks[task_idx].thin[idx], &n->v);
|
||||
tasks[task_idx].thin[idx].key = str8_copy(scratch.arena, tasks[task_idx].thin[idx].key);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
arena_clear(batch->arena);
|
||||
batch->first_wide = batch->last_wide = batch->first_thin = batch->last_thin = 0;
|
||||
batch->wide_count = batch->thin_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
lane_sync_u64(&tasks, 0);
|
||||
lane_sync_u64(&tasks_count, 0);
|
||||
lane_sync();
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: set up artifact cache thread context
|
||||
//
|
||||
ac_tctx = push_array(scratch.arena, AC_TCTX, 1);
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: do all requests
|
||||
//
|
||||
for EachIndex(task_idx, tasks_count)
|
||||
{
|
||||
lane_sync();
|
||||
RequestBatchTask *task = &tasks[task_idx];
|
||||
|
||||
//- rjf: set up cancellation signal
|
||||
U64 cancelled = 0;
|
||||
U64 *cancelled_ptr = 0;
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
cancelled_ptr = &cancelled;
|
||||
}
|
||||
lane_sync_u64(&cancelled_ptr, 0);
|
||||
|
||||
//- rjf: do all wide requests for this priority
|
||||
U64 done_wide_count = 0;
|
||||
ProfScope("wide requests (p%I64u)", task_idx)
|
||||
{
|
||||
for EachIndex(idx, task->wide_count)
|
||||
{
|
||||
lane_sync();
|
||||
AC_Request *r = &task->wide[idx];
|
||||
|
||||
// rjf: any new higher priority tasks? -> cancel
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
if(task_idx == 1 && idx != 0 && ins_atomic_u32_eval(&async_loop_again_high_priority))
|
||||
{
|
||||
ins_atomic_u64_eval_assign(cancelled_ptr, 1);
|
||||
}
|
||||
}
|
||||
lane_sync();
|
||||
|
||||
// rjf: cancelled? -> exit
|
||||
if(ins_atomic_u32_eval(cancelled_ptr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// rjf: compute val
|
||||
AC_Status status = AC_Status_Good;
|
||||
U64 gen = r->gen;
|
||||
AC_Artifact val = r->create(r->key, r->cancel_signal, &status, &gen);
|
||||
|
||||
// rjf: retry? -> resubmit request
|
||||
if(status == AC_Status_NeedRetry && lane_idx() == 0 && !ins_atomic_u32_eval(r->cancel_signal))
|
||||
{
|
||||
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
|
||||
MutexScope(batch->mutex)
|
||||
{
|
||||
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
|
||||
SLLQueuePush(batch->first_wide, batch->last_wide, n);
|
||||
batch->wide_count += 1;
|
||||
MemoryCopyStruct(&n->v, r);
|
||||
n->v.key = str8_copy(batch->arena, n->v.key);
|
||||
}
|
||||
ins_atomic_u32_eval_assign(&async_loop_again, 1);
|
||||
}
|
||||
|
||||
// rjf: create function -> cache
|
||||
AC_Cache *cache = 0;
|
||||
if(status != AC_Status_NeedRetry && lane_idx() == 0)
|
||||
{
|
||||
U64 cache_hash = u64_hash_from_str8(str8_struct(&r->create));
|
||||
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
|
||||
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
|
||||
RWMutexScope(cache_stripe->rw_mutex, 0)
|
||||
{
|
||||
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
|
||||
{
|
||||
if(c->create == r->create)
|
||||
{
|
||||
cache = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: write value into cache
|
||||
if(status != AC_Status_NeedRetry && lane_idx() == 0)
|
||||
{
|
||||
U64 hash = u64_hash_from_str8(r->key);
|
||||
U64 slot_idx = hash%cache->slots_count;
|
||||
AC_Slot *slot = &cache->slots[slot_idx];
|
||||
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
|
||||
RWMutexScope(stripe->rw_mutex, 1)
|
||||
{
|
||||
for(AC_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(str8_match(n->key, r->key, 0))
|
||||
{
|
||||
B32 got_new_value = (status == AC_Status_Good);
|
||||
|
||||
// rjf: eliminate existing values, if any, if they do not match the current
|
||||
if(got_new_value && cache->destroy != 0 && !MemoryMatchStruct(&n->val, &val) && ins_atomic_u64_eval(&n->completion_count) > 0) for(;;)
|
||||
{
|
||||
if(access_pt_is_expired(&n->access_pt, .time = 0, .update_idxs = 0))
|
||||
{
|
||||
cache->destroy(n->val);
|
||||
MemoryZeroStruct(&n->val);
|
||||
break;
|
||||
}
|
||||
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, max_U64);
|
||||
}
|
||||
|
||||
// rjf: write new value
|
||||
if(got_new_value)
|
||||
{
|
||||
n->val = val;
|
||||
}
|
||||
n->last_completed_gen = gen;
|
||||
ins_atomic_u64_dec_eval(&n->working_count);
|
||||
ins_atomic_u64_inc_eval(&n->completion_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
cond_var_broadcast(stripe->cv);
|
||||
}
|
||||
|
||||
// rjf: increment count
|
||||
lane_sync();
|
||||
done_wide_count += 1;
|
||||
}
|
||||
}
|
||||
lane_sync();
|
||||
|
||||
//- rjf: do all thin requests for this priority
|
||||
U64 done_thin_count = 0;
|
||||
ProfScope("thin requests (p%I64u)", task_idx)
|
||||
{
|
||||
U64 req_take_counter = 0;
|
||||
U64 *req_take_counter_ptr = 0;
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
req_take_counter_ptr = &req_take_counter;
|
||||
}
|
||||
lane_sync_u64(&req_take_counter_ptr, 0);
|
||||
for(;;)
|
||||
{
|
||||
// rjf: any new higher priority tasks? -> cancel
|
||||
if(task_idx == 1 && ins_atomic_u64_eval(req_take_counter_ptr) >= task->thin_count/2 &&
|
||||
ins_atomic_u32_eval(&async_loop_again_high_priority))
|
||||
{
|
||||
ins_atomic_u64_eval_assign(cancelled_ptr, 1);
|
||||
}
|
||||
|
||||
// rjf: cancelled? -> exit
|
||||
if(ins_atomic_u64_eval(cancelled_ptr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// rjf: take next task
|
||||
U64 req_idx = ins_atomic_u64_inc_eval(req_take_counter_ptr) - 1;
|
||||
if(req_idx >= task->thin_count) { break; }
|
||||
AC_Request *r = &task->thin[req_idx];
|
||||
|
||||
// rjf: push thin lane ctx
|
||||
U64 thin_lane_ctx_broadcast_memory = 0;
|
||||
LaneCtx thin_lane_ctx = {0, 1, {0}, &thin_lane_ctx_broadcast_memory};
|
||||
LaneCtx lane_ctx_restore = lane_ctx(thin_lane_ctx);
|
||||
|
||||
// rjf: compute val
|
||||
AC_Status status = AC_Status_Good;
|
||||
U64 gen = r->gen;
|
||||
AC_Artifact val = r->create(r->key, r->cancel_signal, &status, &gen);
|
||||
|
||||
// rjf: restore wide lane ctx
|
||||
lane_ctx(lane_ctx_restore);
|
||||
|
||||
// rjf: retry? -> resubmit request
|
||||
if(status == AC_Status_NeedRetry && !ins_atomic_u32_eval(r->cancel_signal))
|
||||
{
|
||||
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
|
||||
MutexScope(batch->mutex)
|
||||
{
|
||||
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
|
||||
SLLQueuePush(batch->first_thin, batch->last_thin, n);
|
||||
batch->thin_count += 1;
|
||||
MemoryCopyStruct(&n->v, r);
|
||||
n->v.key = str8_copy(batch->arena, n->v.key);
|
||||
}
|
||||
ins_atomic_u32_eval_assign(&async_loop_again, 1);
|
||||
}
|
||||
|
||||
// rjf: create function -> cache
|
||||
AC_Cache *cache = 0;
|
||||
if(status != AC_Status_NeedRetry)
|
||||
{
|
||||
U64 cache_hash = u64_hash_from_str8(str8_struct(&r->create));
|
||||
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
|
||||
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
|
||||
RWMutexScope(cache_stripe->rw_mutex, 0)
|
||||
{
|
||||
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
|
||||
{
|
||||
if(c->create == r->create)
|
||||
{
|
||||
cache = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: write value into cache
|
||||
if(status != AC_Status_NeedRetry)
|
||||
{
|
||||
U64 hash = u64_hash_from_str8(r->key);
|
||||
U64 slot_idx = hash%cache->slots_count;
|
||||
AC_Slot *slot = &cache->slots[slot_idx];
|
||||
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
|
||||
RWMutexScope(stripe->rw_mutex, 1)
|
||||
{
|
||||
for(AC_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(str8_match(n->key, r->key, 0))
|
||||
{
|
||||
B32 got_new_value = (status == AC_Status_Good);
|
||||
|
||||
// rjf: eliminate existing values, if any, if they do not match the current
|
||||
if(got_new_value && cache->destroy != 0 && !MemoryMatchStruct(&n->val, &val) && ins_atomic_u64_eval(&n->completion_count) > 0) for(;;)
|
||||
{
|
||||
if(access_pt_is_expired(&n->access_pt, .time = 0, .update_idxs = 0))
|
||||
{
|
||||
cache->destroy(n->val);
|
||||
MemoryZeroStruct(&n->val);
|
||||
break;
|
||||
}
|
||||
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, max_U64);
|
||||
}
|
||||
|
||||
// rjf: store
|
||||
if(got_new_value)
|
||||
{
|
||||
n->val = val;
|
||||
}
|
||||
n->last_completed_gen = gen;
|
||||
ins_atomic_u64_dec_eval(&n->working_count);
|
||||
ins_atomic_u64_inc_eval(&n->completion_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
cond_var_broadcast(stripe->cv);
|
||||
}
|
||||
}
|
||||
lane_sync();
|
||||
done_thin_count = ins_atomic_u64_eval(req_take_counter_ptr);
|
||||
lane_sync();
|
||||
}
|
||||
|
||||
//- rjf: cancelled early, unfinished tasks? -> defer to next tick
|
||||
if(lane_idx() == 0 && task_idx > 0)
|
||||
{
|
||||
B32 need_another_try = (done_wide_count < task->wide_count || done_thin_count < task->thin_count);
|
||||
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
|
||||
MutexScope(batch->mutex)
|
||||
{
|
||||
// rjf: push leftover wide tasks
|
||||
for(U64 idx = done_wide_count; idx < task->wide_count; idx += 1)
|
||||
{
|
||||
AC_Request *r = &task->wide[idx];
|
||||
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
|
||||
SLLQueuePush(batch->first_wide, batch->last_wide, n);
|
||||
batch->wide_count += 1;
|
||||
MemoryCopyStruct(&n->v, r);
|
||||
n->v.key = str8_copy(batch->arena, n->v.key);
|
||||
}
|
||||
|
||||
// rjf: push leftover thin tasks
|
||||
for(U64 idx = done_thin_count; idx < task->thin_count; idx += 1)
|
||||
{
|
||||
AC_Request *r = &task->thin[idx];
|
||||
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
|
||||
SLLQueuePush(batch->first_thin, batch->last_thin, n);
|
||||
batch->thin_count += 1;
|
||||
MemoryCopyStruct(&n->v, r);
|
||||
n->v.key = str8_copy(batch->arena, n->v.key);
|
||||
}
|
||||
}
|
||||
if(need_another_try)
|
||||
{
|
||||
ins_atomic_u32_eval_assign(&async_loop_again, 1);
|
||||
}
|
||||
}
|
||||
lane_sync();
|
||||
}
|
||||
lane_sync();
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: increment the request gen, only if all requests are empty
|
||||
//
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
B32 have_live_requests = 0;
|
||||
for EachElement(idx, ac_shared->req_batches)
|
||||
{
|
||||
MutexScope(ac_shared->req_batches[idx].mutex)
|
||||
{
|
||||
have_live_requests = (ac_shared->req_batches[idx].wide_count != 0 || ac_shared->req_batches[idx].thin_count != 0);
|
||||
}
|
||||
}
|
||||
if(!have_live_requests)
|
||||
{
|
||||
ac_shared->request_gen += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: reset thread context
|
||||
//
|
||||
ac_tctx = 0;
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: disable cancellation scanning
|
||||
//
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
semaphore_take(ac_shared->cancel_thread_semaphore, max_U64);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cancel Thread
|
||||
|
||||
internal void
|
||||
ac_cancel_thread_entry_point(void *p)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
sleep_ms(50);
|
||||
semaphore_take(ac_shared->cancel_thread_semaphore, max_U64);
|
||||
{
|
||||
for EachIndex(cache_slot_idx, ac_shared->cache_slots_count)
|
||||
{
|
||||
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
|
||||
RWMutexScope(cache_stripe->rw_mutex, 0)
|
||||
{
|
||||
for EachNode(cache, AC_Cache, ac_shared->cache_slots[cache_slot_idx])
|
||||
{
|
||||
Rng1U64 slot_range = lane_range(cache->slots_count);
|
||||
for EachInRange(slot_idx, slot_range)
|
||||
{
|
||||
AC_Slot *slot = &cache->slots[slot_idx];
|
||||
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
|
||||
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
|
||||
{
|
||||
B32 slot_has_work = 0;
|
||||
RWMutexScope(stripe->rw_mutex, write_mode)
|
||||
{
|
||||
for(AC_Node *n = slot->first, *next = 0; n != 0; n = next)
|
||||
{
|
||||
next = n->next;
|
||||
if(access_pt_is_expired(&n->access_pt, .time = n->evict_threshold_us) && ins_atomic_u64_eval(&n->working_count) > 0)
|
||||
{
|
||||
slot_has_work = 1;
|
||||
if(!write_mode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
n->cancelled = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!slot_has_work)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
semaphore_drop(ac_shared->cancel_thread_semaphore);
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef ARTIFACT_CACHE_H
|
||||
#define ARTIFACT_CACHE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Artifact Computation Statuses
|
||||
|
||||
typedef enum AC_Status
|
||||
{
|
||||
AC_Status_Good,
|
||||
AC_Status_NeedRetry,
|
||||
AC_Status_Failed,
|
||||
}
|
||||
AC_Status;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Artifact Handle Type
|
||||
|
||||
typedef struct AC_Artifact AC_Artifact;
|
||||
struct AC_Artifact
|
||||
{
|
||||
U64 u64[4];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Artifact Computation Function Types
|
||||
|
||||
typedef AC_Artifact AC_CreateFunctionType(String8 key, B32 *cancel_signal, AC_Status *status_out, U64 *gen_out);
|
||||
typedef void AC_DestroyFunctionType(AC_Artifact artifact);
|
||||
|
||||
typedef U32 AC_Flags;
|
||||
typedef enum AC_FlagsEnum
|
||||
{
|
||||
AC_Flag_WaitForFresh = (1<<0),
|
||||
AC_Flag_HighPriority = (1<<1),
|
||||
AC_Flag_Wide = (1<<2),
|
||||
}
|
||||
AC_FlagsEnum;
|
||||
|
||||
typedef struct AC_ArtifactParams AC_ArtifactParams;
|
||||
struct AC_ArtifactParams
|
||||
{
|
||||
AC_CreateFunctionType *create;
|
||||
AC_DestroyFunctionType *destroy;
|
||||
U64 slots_count;
|
||||
U64 gen;
|
||||
U64 evict_threshold_us;
|
||||
B32 *stale_out;
|
||||
AC_Flags flags;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Types
|
||||
|
||||
typedef struct AC_Request AC_Request;
|
||||
struct AC_Request
|
||||
{
|
||||
String8 key;
|
||||
U64 gen;
|
||||
B32 *cancel_signal;
|
||||
AC_CreateFunctionType *create;
|
||||
};
|
||||
|
||||
typedef struct AC_RequestNode AC_RequestNode;
|
||||
struct AC_RequestNode
|
||||
{
|
||||
AC_RequestNode *next;
|
||||
AC_Request v;
|
||||
};
|
||||
|
||||
typedef struct AC_NodePtr AC_NodePtr;
|
||||
struct AC_NodePtr
|
||||
{
|
||||
AC_NodePtr *next;
|
||||
struct AC_Node *node;
|
||||
};
|
||||
|
||||
typedef struct AC_Node AC_Node;
|
||||
struct AC_Node
|
||||
{
|
||||
AC_Node *next;
|
||||
AC_Node *prev;
|
||||
|
||||
// rjf: key/gen/value
|
||||
String8 key;
|
||||
U64 last_requested_gen;
|
||||
U64 last_completed_gen;
|
||||
AC_Artifact val;
|
||||
|
||||
// rjf: metadata
|
||||
AccessPt access_pt;
|
||||
U64 working_count;
|
||||
U64 completion_count;
|
||||
U64 evict_threshold_us;
|
||||
U64 last_touched_ac_request_gen;
|
||||
B32 cancelled;
|
||||
B32 other_nodes_depend_on_me;
|
||||
};
|
||||
|
||||
typedef struct AC_Slot AC_Slot;
|
||||
struct AC_Slot
|
||||
{
|
||||
AC_Node *first;
|
||||
AC_Node *last;
|
||||
};
|
||||
|
||||
typedef struct AC_Cache AC_Cache;
|
||||
struct AC_Cache
|
||||
{
|
||||
// rjf: link / key for cache-cache
|
||||
AC_Cache *next;
|
||||
AC_CreateFunctionType *create;
|
||||
AC_DestroyFunctionType *destroy;
|
||||
|
||||
// rjf: artifact cache
|
||||
U64 slots_count;
|
||||
AC_Slot *slots;
|
||||
StripeArray stripes;
|
||||
AC_NodePtr **stripe_free_node_ptrs;
|
||||
};
|
||||
|
||||
typedef struct AC_RequestBatch AC_RequestBatch;
|
||||
struct AC_RequestBatch
|
||||
{
|
||||
Mutex mutex;
|
||||
Arena *arena;
|
||||
AC_RequestNode *first_wide;
|
||||
AC_RequestNode *last_wide;
|
||||
AC_RequestNode *first_thin;
|
||||
AC_RequestNode *last_thin;
|
||||
U64 wide_count;
|
||||
U64 thin_count;
|
||||
};
|
||||
|
||||
typedef struct AC_TCTX AC_TCTX;
|
||||
struct AC_TCTX
|
||||
{
|
||||
U64 _unused_;
|
||||
};
|
||||
|
||||
typedef struct AC_Shared AC_Shared;
|
||||
struct AC_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
U64 request_gen;
|
||||
|
||||
// rjf: cache cache
|
||||
U64 cache_slots_count;
|
||||
AC_Cache **cache_slots;
|
||||
StripeArray cache_stripes;
|
||||
|
||||
// rjf: requests
|
||||
AC_RequestBatch req_batches[2]; // 0: high priority, 1: low priority
|
||||
|
||||
// rjf: cancel thread
|
||||
Thread cancel_thread;
|
||||
Semaphore cancel_thread_semaphore;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global AC_Shared *ac_shared = 0;
|
||||
thread_static AC_TCTX *ac_tctx = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Layer Initialization
|
||||
|
||||
internal void ac_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal AC_Artifact ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U64 endt_us);
|
||||
#define ac_artifact_from_key(access, key, create_fn, destroy_fn, endt_us, ...) ac_artifact_from_key_((access), (key), &(AC_ArtifactParams){.create = (create_fn), .destroy = (destroy_fn), .evict_threshold_us = (2000000), __VA_ARGS__}, (endt_us))
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Tick
|
||||
|
||||
#if !defined(NEED_ASYNC)
|
||||
# define NEED_ASYNC 1
|
||||
#endif
|
||||
internal void ac_async_tick(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cancel Thread
|
||||
|
||||
internal void ac_cancel_thread_entry_point(void *p);
|
||||
|
||||
#endif // ARTIFACT_CACHE_H
|
||||
+302
-390
@@ -1,390 +1,302 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Global Arena Table
|
||||
|
||||
#if ARENA_TABLE_DEBUG
|
||||
typedef struct ArenaTableNode ArenaTableNode;
|
||||
struct ArenaTableNode
|
||||
{
|
||||
ArenaTableNode *next;
|
||||
Arena *arena;
|
||||
};
|
||||
global U64 arena_table_take_init = 0;
|
||||
global U64 arena_table_inited = 0;
|
||||
global U64 arena_table_lock = 0;
|
||||
global ArenaTableNode *arena_table = 0;
|
||||
global U64 arena_table_count = 0;
|
||||
global U64 arena_table_cap = 0;
|
||||
global ArenaTableNode *free_arena_table_node = 0;
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arena Functions
|
||||
|
||||
//- rjf: arena creation/destruction
|
||||
|
||||
internal Arena *
|
||||
arena_alloc_(ArenaParams *params)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
|
||||
U64 reserve_size = params->reserve_size;
|
||||
U64 commit_size = params->commit_size;
|
||||
|
||||
// rjf: reserve/commit initial block
|
||||
void *base = params->optional_backing_buffer;
|
||||
if(base == 0)
|
||||
{
|
||||
// rjf: round up reserve/commit sizes
|
||||
if(params->flags & ArenaFlag_LargePages)
|
||||
{
|
||||
reserve_size = AlignPow2(reserve_size, get_system_info()->large_page_size);
|
||||
commit_size = AlignPow2(commit_size, get_system_info()->large_page_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
reserve_size = AlignPow2(reserve_size, get_system_info()->page_size);
|
||||
commit_size = AlignPow2(commit_size, get_system_info()->page_size);
|
||||
}
|
||||
|
||||
if(params->flags & ArenaFlag_LargePages)
|
||||
{
|
||||
base = reserve_memory_large(reserve_size);
|
||||
commit_memory_large(base, commit_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
base = reserve_memory(reserve_size);
|
||||
commit_memory(base, commit_size);
|
||||
}
|
||||
AsanPoisonMemoryRegion(base, commit_size);
|
||||
// TODO(rjf): we need to reintroduce this later when we have the ability to remove annotations...
|
||||
// raddbg_annotate_vaddr_range(base, reserve_size, "arena %s:%i", params->allocation_site_file, params->allocation_site_line);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsanPoisonMemoryRegion(base, params->reserve_size);
|
||||
}
|
||||
|
||||
// rjf: panic on arena creation failure
|
||||
#if OS_FEATURE_GRAPHICAL
|
||||
if(Unlikely(base == 0))
|
||||
{
|
||||
wm_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
|
||||
abort_self(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// rjf: extract arena header & fill
|
||||
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
|
||||
Arena *arena = base;
|
||||
arena->current = arena;
|
||||
arena->flags = params->flags;
|
||||
arena->cmt_size = params->commit_size;
|
||||
arena->res_size = params->reserve_size;
|
||||
arena->base_pos = 0;
|
||||
arena->pos = ARENA_HEADER_SIZE;
|
||||
arena->cmt = commit_size;
|
||||
arena->res = reserve_size;
|
||||
arena->allocation_site_file = params->allocation_site_file;
|
||||
arena->allocation_site_line = params->allocation_site_line;
|
||||
arena->name = params->name;
|
||||
#if ARENA_FREE_LIST
|
||||
arena->free_last = 0;
|
||||
#endif
|
||||
|
||||
// rjf: store in global arena table
|
||||
#if ARENA_TABLE_DEBUG
|
||||
if(ins_atomic_u64_eval_cond_assign(&arena_table_take_init, 1, 0) == 0)
|
||||
{
|
||||
arena_table = reserve_memory(GB(256));
|
||||
arena_table_cap = 4096;
|
||||
commit_memory(arena_table, arena_table_cap * sizeof(arena_table[0]));
|
||||
ins_atomic_u64_inc_eval(&arena_table_inited);
|
||||
}
|
||||
for(;!ins_atomic_u64_eval(&arena_table_inited);) {}
|
||||
for(;;)
|
||||
{
|
||||
B32 got_lock = (ins_atomic_u64_eval_cond_assign(&arena_table_lock, 1, 0) == 0);
|
||||
if(got_lock)
|
||||
{
|
||||
ArenaTableNode *node = free_arena_table_node;
|
||||
if(node != 0)
|
||||
{
|
||||
SLLStackPop(free_arena_table_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = &arena_table[arena_table_count];
|
||||
if(arena_table_count >= arena_table_cap)
|
||||
{
|
||||
U64 arena_table_cap__pre_grow = arena_table_cap;
|
||||
arena_table_cap *= 2;
|
||||
U64 arena_table_cap__post_grow = arena_table_cap;
|
||||
commit_memory(arena_table + arena_table_cap__pre_grow, sizeof(arena_table[0]) * (arena_table_cap__post_grow - arena_table_cap__pre_grow));
|
||||
}
|
||||
arena_table_count += 1;
|
||||
}
|
||||
node->arena = arena;
|
||||
arena->table_node = node;
|
||||
ins_atomic_u64_eval_assign(&arena_table_lock, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ProfEnd();
|
||||
return arena;
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_release(Arena *arena)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
|
||||
#if PROFILE_TELEMETRY
|
||||
{
|
||||
Arena *base_arena = arena;
|
||||
while (base_arena->prev) base_arena = base_arena->prev;
|
||||
tmPlot(0, TM_PLOT_UNITS_MEMORY, TM_PLOT_DRAW_LINE, 0, "%s/%p", base_arena->name, base_arena);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ARENA_TABLE_DEBUG
|
||||
for(Arena *n = arena->current; n != 0; n = n->prev)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
B32 got_lock = (ins_atomic_u64_eval_cond_assign(&arena_table_lock, 1, 0) == 0);
|
||||
if(got_lock)
|
||||
{
|
||||
ArenaTableNode *table_node = n->table_node;
|
||||
MemoryZeroStruct(table_node);
|
||||
SLLStackPush(free_arena_table_node, table_node);
|
||||
ins_atomic_u64_eval_assign(&arena_table_lock, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for(Arena *n = arena->current, *prev = 0; n != 0; n = prev)
|
||||
{
|
||||
prev = n->prev;
|
||||
AsanUnpoisonMemoryRegion(n, n->cmt);
|
||||
release_memory(n, n->res);
|
||||
}
|
||||
|
||||
ProfEnd();
|
||||
}
|
||||
|
||||
//- rjf: arena push/pop core functions
|
||||
|
||||
internal void *
|
||||
arena_push(Arena *arena, U64 size, U64 align, B32 zero)
|
||||
{
|
||||
Arena *current = arena->current;
|
||||
U64 pos_pre = AlignPow2(current->pos, align);
|
||||
U64 pos_pst = pos_pre + size;
|
||||
|
||||
// TODO: Newly allocated arenas already have zeroed commited pages,
|
||||
// so this unnecessarily zeroes them again.
|
||||
//
|
||||
// rjf: compute the size we need to zero
|
||||
U64 size_to_zero = 0;
|
||||
if(zero)
|
||||
{
|
||||
size_to_zero = Min(current->cmt, pos_pst) - pos_pre;
|
||||
}
|
||||
|
||||
// rjf: chain, if needed
|
||||
if(current->res < pos_pst && !(arena->flags & ArenaFlag_NoChain))
|
||||
{
|
||||
Arena *new_block = 0;
|
||||
|
||||
#if ARENA_FREE_LIST
|
||||
{
|
||||
Arena *prev_block;
|
||||
for(new_block = arena->free_last, prev_block = 0; new_block != 0; prev_block = new_block, new_block = new_block->prev)
|
||||
{
|
||||
if(new_block->res >= AlignPow2(new_block->pos, align) + size)
|
||||
{
|
||||
if(prev_block)
|
||||
{
|
||||
prev_block->prev = new_block->prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
arena->free_last = new_block->prev;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(new_block == 0)
|
||||
{
|
||||
U64 res_size = current->res_size;
|
||||
U64 cmt_size = current->cmt_size;
|
||||
if(size + ARENA_HEADER_SIZE > res_size)
|
||||
{
|
||||
res_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
|
||||
cmt_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
|
||||
}
|
||||
new_block = arena_alloc(.reserve_size = res_size,
|
||||
.commit_size = cmt_size,
|
||||
.flags = current->flags,
|
||||
.allocation_site_file = current->allocation_site_file,
|
||||
.allocation_site_line = current->allocation_site_line);
|
||||
|
||||
size_to_zero = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_to_zero = size;
|
||||
}
|
||||
|
||||
new_block->base_pos = current->base_pos + current->res;
|
||||
SLLStackPush_N(arena->current, new_block, prev);
|
||||
|
||||
current = new_block;
|
||||
pos_pre = AlignPow2(current->pos, align);
|
||||
pos_pst = pos_pre + size;
|
||||
}
|
||||
|
||||
// rjf: commit new pages, if needed
|
||||
if(current->cmt < pos_pst)
|
||||
{
|
||||
U64 cmt_pst_aligned = pos_pst + current->cmt_size-1;
|
||||
cmt_pst_aligned -= cmt_pst_aligned%current->cmt_size;
|
||||
U64 cmt_pst_clamped = ClampTop(cmt_pst_aligned, current->res);
|
||||
U64 cmt_size = cmt_pst_clamped - current->cmt;
|
||||
U8 *cmt_ptr = (U8 *)current + current->cmt;
|
||||
if(current->flags & ArenaFlag_LargePages)
|
||||
{
|
||||
commit_memory_large(cmt_ptr, cmt_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
commit_memory(cmt_ptr, cmt_size);
|
||||
}
|
||||
AsanPoisonMemoryRegion(cmt_ptr, cmt_size);
|
||||
current->cmt = cmt_pst_clamped;
|
||||
}
|
||||
|
||||
// rjf: push onto current block
|
||||
void *result = 0;
|
||||
if(current->cmt >= pos_pst)
|
||||
{
|
||||
result = (U8 *)current+pos_pre;
|
||||
current->pos = pos_pst;
|
||||
AsanUnpoisonMemoryRegion(result, size);
|
||||
MemoryZero(result, size_to_zero);
|
||||
}
|
||||
|
||||
#if PROFILE_TELEMETRY
|
||||
if(size > KB(1))
|
||||
{
|
||||
Arena *base_arena = arena;
|
||||
while (base_arena->prev) base_arena = base_arena->prev;
|
||||
tmPlot(0, TM_PLOT_UNITS_MEMORY, TM_PLOT_DRAW_LINE, (double)(current->base_pos + current->pos) / 1024.0 / 1024.0, "%s/%p", base_arena->name, base_arena);
|
||||
}
|
||||
#endif
|
||||
|
||||
// rjf: panic on failure
|
||||
#if OS_FEATURE_GRAPHICAL
|
||||
if(Unlikely(result == 0))
|
||||
{
|
||||
wm_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
|
||||
abort_self(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
arena_pos(Arena *arena)
|
||||
{
|
||||
Arena *current = arena->current;
|
||||
U64 pos = current->base_pos + current->pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_pop_to(Arena *arena, U64 pos)
|
||||
{
|
||||
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, pos);
|
||||
Arena *current = arena->current;
|
||||
|
||||
#if ARENA_FREE_LIST
|
||||
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
|
||||
{
|
||||
prev = current->prev;
|
||||
current->pos = ARENA_HEADER_SIZE;
|
||||
SLLStackPush_N(arena->free_last, current, prev);
|
||||
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res - ARENA_HEADER_SIZE);
|
||||
}
|
||||
#else
|
||||
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
|
||||
{
|
||||
prev = current->prev;
|
||||
AsanUnpoisonMemoryRegion(current, current->cmt);
|
||||
release_memory(current, current->res);
|
||||
}
|
||||
#endif
|
||||
arena->current = current;
|
||||
U64 new_pos = big_pos - current->base_pos;
|
||||
AssertAlways(new_pos <= current->pos);
|
||||
AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos));
|
||||
current->pos = new_pos;
|
||||
|
||||
#if PROFILE_TELEMETRY
|
||||
if((pos - (new_pos + current->base_pos)) > KB(1))
|
||||
{
|
||||
Arena *base_arena = arena;
|
||||
while (base_arena->prev) base_arena = base_arena->prev;
|
||||
tmPlot(0, TM_PLOT_UNITS_MEMORY, TM_PLOT_DRAW_LINE, (double)(current->base_pos + current->pos) / 1024.0 / 1024.0, "%s/%p", base_arena->name, base_arena);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//- rjf: arena push/pop helpers
|
||||
|
||||
internal void
|
||||
arena_clear(Arena *arena)
|
||||
{
|
||||
arena_pop_to(arena, 0);
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_pop(Arena *arena, U64 amt)
|
||||
{
|
||||
U64 pos_old = arena_pos(arena);
|
||||
U64 pos_new = pos_old;
|
||||
if(amt < pos_old)
|
||||
{
|
||||
pos_new = pos_old - amt;
|
||||
}
|
||||
arena_pop_to(arena, pos_new);
|
||||
}
|
||||
|
||||
//- rjf: temporary arena scopes
|
||||
|
||||
internal Temp
|
||||
temp_begin(Arena *arena)
|
||||
{
|
||||
U64 pos = arena_pos(arena);
|
||||
Temp temp = {arena, pos};
|
||||
return temp;
|
||||
}
|
||||
|
||||
internal void
|
||||
temp_end(Temp temp)
|
||||
{
|
||||
arena_pop_to(temp.arena, temp.pos);
|
||||
}
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
// Implementation
|
||||
|
||||
internal Arena *
|
||||
arena_alloc__sized(U64 init_res, U64 init_cmt)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
Assert(ARENA_HEADER_SIZE < init_cmt && init_cmt <= init_res);
|
||||
|
||||
void *memory = 0;
|
||||
U64 res = 0;
|
||||
U64 cmt = 0;
|
||||
B32 large_pages = os_large_pages_enabled();
|
||||
if(large_pages)
|
||||
{
|
||||
U64 page_size = os_large_page_size();
|
||||
res = AlignPow2(init_res, page_size);
|
||||
#if OS_WINDOWS
|
||||
cmt = res;
|
||||
#else
|
||||
cmt = AlignPow2(init_cmt, page_size);
|
||||
#endif
|
||||
memory = os_reserve_large(res);
|
||||
if(!os_commit_large(memory, cmt))
|
||||
{
|
||||
memory = 0;
|
||||
os_release(memory, res);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
U64 page_size = os_page_size();
|
||||
res = AlignPow2(init_res, page_size);
|
||||
cmt = AlignPow2(init_cmt, page_size);
|
||||
memory = os_reserve(res);
|
||||
if(!os_commit(memory, cmt))
|
||||
{
|
||||
memory = 0;
|
||||
os_release(memory, res);
|
||||
}
|
||||
}
|
||||
|
||||
Arena *arena = (Arena*)memory;
|
||||
if(arena)
|
||||
{
|
||||
AsanPoisonMemoryRegion(memory, cmt);
|
||||
AsanUnpoisonMemoryRegion(memory, ARENA_HEADER_SIZE);
|
||||
arena->prev = 0;
|
||||
arena->current = arena;
|
||||
arena->base_pos = 0;
|
||||
arena->pos = ARENA_HEADER_SIZE;
|
||||
arena->cmt = cmt;
|
||||
arena->res = res;
|
||||
arena->align = 8;
|
||||
arena->grow = 1;
|
||||
arena->large_pages = large_pages;
|
||||
}
|
||||
|
||||
ProfEnd();
|
||||
return arena;
|
||||
}
|
||||
|
||||
internal Arena *
|
||||
arena_alloc(void)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
|
||||
U64 init_res, init_cmt;
|
||||
if (os_large_pages_enabled()) {
|
||||
init_res = ARENA_RESERVE_SIZE_LARGE_PAGES;
|
||||
init_cmt = ARENA_COMMIT_SIZE_LARGE_PAGES;
|
||||
} else {
|
||||
init_res = ARENA_RESERVE_SIZE;
|
||||
init_cmt = ARENA_COMMIT_SIZE;
|
||||
}
|
||||
|
||||
Arena *arena = arena_alloc__sized(init_res, init_cmt);
|
||||
|
||||
ProfEnd();
|
||||
return arena;
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_release(Arena *arena)
|
||||
{
|
||||
for (Arena *node = arena->current, *prev = 0; node != 0; node = prev) {
|
||||
prev = node->prev;
|
||||
os_release(node, node->res);
|
||||
}
|
||||
}
|
||||
|
||||
internal U64
|
||||
arena_huge_push_threshold(void)
|
||||
{
|
||||
U64 reserve_size = os_large_pages_enabled() ? ARENA_RESERVE_SIZE_LARGE_PAGES : ARENA_RESERVE_SIZE;
|
||||
U64 threshold = (reserve_size - ARENA_HEADER_SIZE) / 2 + 1;
|
||||
return threshold;
|
||||
}
|
||||
|
||||
internal void *
|
||||
arena_push__impl(Arena *arena, U64 size)
|
||||
{
|
||||
Arena *current = arena->current;
|
||||
U64 pos_mem = AlignPow2(current->pos, arena->align);
|
||||
U64 pos_new = pos_mem + size;
|
||||
|
||||
if (current->res < pos_new && arena->grow) {
|
||||
Arena *new_block;
|
||||
|
||||
// normal growth path
|
||||
if (size < arena_huge_push_threshold()) {
|
||||
new_block = arena_alloc();
|
||||
}
|
||||
// huge growth path
|
||||
else {
|
||||
U64 new_block_size = size + ARENA_HEADER_SIZE;
|
||||
new_block = arena_alloc__sized(new_block_size, new_block_size);
|
||||
}
|
||||
|
||||
if (new_block) {
|
||||
new_block->base_pos = current->base_pos + current->res;
|
||||
SLLStackPush_N(arena->current, new_block, prev);
|
||||
|
||||
current = new_block;
|
||||
pos_mem = AlignPow2(current->pos, current->align);
|
||||
pos_new = pos_mem + size;
|
||||
}
|
||||
}
|
||||
|
||||
if (current->cmt < pos_new) {
|
||||
U64 cmt_new_aligned, cmt_new_clamped, cmt_new_size;
|
||||
B32 is_cmt_ok;
|
||||
|
||||
if (current->large_pages) {
|
||||
cmt_new_aligned = AlignPow2(pos_new, ARENA_COMMIT_SIZE_LARGE_PAGES);
|
||||
cmt_new_clamped = ClampTop(cmt_new_aligned, current->res);
|
||||
cmt_new_size = cmt_new_clamped - current->cmt;
|
||||
is_cmt_ok = os_commit_large((U8*)current + current->cmt, cmt_new_size);
|
||||
} else {
|
||||
cmt_new_aligned = AlignPow2(pos_new, ARENA_COMMIT_SIZE);
|
||||
cmt_new_clamped = ClampTop(cmt_new_aligned, current->res);
|
||||
cmt_new_size = cmt_new_clamped - current->cmt;
|
||||
is_cmt_ok = os_commit((U8*)current + current->cmt, cmt_new_size);
|
||||
}
|
||||
|
||||
if (is_cmt_ok) {
|
||||
current->cmt = cmt_new_clamped;
|
||||
}
|
||||
}
|
||||
|
||||
void *memory = 0;
|
||||
|
||||
if (current->cmt >= pos_new) {
|
||||
memory = (U8*)current + pos_mem;
|
||||
current->pos = pos_new;
|
||||
AsanUnpoisonMemoryRegion(memory, size);
|
||||
}
|
||||
|
||||
#if OS_FEATURE_GRAPHICAL
|
||||
if(Unlikely(memory == 0))
|
||||
{
|
||||
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
|
||||
os_exit_process(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
return memory;
|
||||
}
|
||||
|
||||
internal U64
|
||||
arena_pos(Arena *arena)
|
||||
{
|
||||
Arena *current = arena->current;
|
||||
U64 pos = current->base_pos + current->pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_pop_to(Arena *arena, U64 big_pos_unclamped)
|
||||
{
|
||||
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, big_pos_unclamped);
|
||||
|
||||
// unroll the chain
|
||||
Arena *current = arena->current;
|
||||
for (Arena *prev = 0; current->base_pos >= big_pos; current = prev) {
|
||||
prev = current->prev;
|
||||
os_release(current, current->res);
|
||||
}
|
||||
AssertAlways(current);
|
||||
arena->current = current;
|
||||
|
||||
// compute arena-relative position
|
||||
U64 new_pos = big_pos - current->base_pos;
|
||||
AssertAlways(new_pos <= current->pos);
|
||||
|
||||
// poison popped memory block
|
||||
AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos));
|
||||
|
||||
// update position
|
||||
current->pos = new_pos;
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_absorb(Arena *arena, Arena *sub)
|
||||
{
|
||||
// base adjustment
|
||||
Arena *current = arena->current;
|
||||
U64 base_adjust = current->base_pos + current->res;
|
||||
for (Arena *node = sub->current; node != 0; node = node->prev) {
|
||||
node->base_pos += base_adjust;
|
||||
}
|
||||
|
||||
// attach sub to arena
|
||||
sub->prev = arena->current;
|
||||
arena->current = sub->current;
|
||||
sub->current = sub;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Wrappers
|
||||
|
||||
internal void *
|
||||
arena_push(Arena *arena, U64 size)
|
||||
{
|
||||
void *memory = arena_push__impl(arena, size);
|
||||
return memory;
|
||||
}
|
||||
|
||||
internal void *
|
||||
arena_push_contiguous(Arena *arena, U64 size)
|
||||
{
|
||||
B32 restore = arena->grow;
|
||||
arena->grow = 0;
|
||||
void *memory = arena_push(arena, size);
|
||||
arena->grow = restore;
|
||||
return memory;
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_push_align(Arena *arena, U64 align)
|
||||
{
|
||||
Assert(IsPow2(align));
|
||||
U64 amt = AlignPadPow2(arena->pos, align);
|
||||
void *ptr = arena_push(arena, amt);
|
||||
MemoryZero(ptr, amt);
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_put_back(Arena *arena, U64 amt)
|
||||
{
|
||||
U64 pos_old = arena_pos(arena);
|
||||
U64 pos_new = pos_old;
|
||||
if (amt < pos_old) {
|
||||
pos_new = pos_old - amt;
|
||||
}
|
||||
arena_pop_to(arena, pos_new);
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_clear(Arena *arena)
|
||||
{
|
||||
arena_pop_to(arena, 0);
|
||||
}
|
||||
|
||||
internal Temp
|
||||
temp_begin(Arena *arena)
|
||||
{
|
||||
U64 pos = arena_pos(arena);
|
||||
Temp temp = {arena, pos};
|
||||
return temp;
|
||||
}
|
||||
|
||||
internal void
|
||||
temp_end(Temp temp)
|
||||
{
|
||||
arena_pop_to(temp.arena, temp.pos);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(allen): "Mini-Arena" Helper
|
||||
|
||||
internal B32
|
||||
ensure_commit(void **cmtptr, void *pos, U64 cmt_block_size){
|
||||
B32 result = 0;
|
||||
U8 *cmt = (U8*)*cmtptr;
|
||||
if (cmt < (U8*)pos){
|
||||
U64 cmt_size_raw = (U8*)pos - cmt;
|
||||
U64 cmt_size = AlignPow2(cmt_size_raw, cmt_block_size);
|
||||
if (os_commit(cmt, cmt_size)){
|
||||
*cmtptr = cmt + cmt_size;
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
||||
+87
-93
@@ -1,93 +1,87 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_ARENA_H
|
||||
#define BASE_ARENA_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arena Types
|
||||
|
||||
#define ARENA_HEADER_SIZE 128
|
||||
|
||||
typedef U64 ArenaFlags;
|
||||
enum
|
||||
{
|
||||
ArenaFlag_NoChain = (1<<0),
|
||||
ArenaFlag_LargePages = (1<<1),
|
||||
};
|
||||
|
||||
typedef struct ArenaParams ArenaParams;
|
||||
struct ArenaParams
|
||||
{
|
||||
ArenaFlags flags;
|
||||
U64 reserve_size;
|
||||
U64 commit_size;
|
||||
void *optional_backing_buffer;
|
||||
char *allocation_site_file;
|
||||
int allocation_site_line;
|
||||
char *name;
|
||||
};
|
||||
|
||||
typedef struct Arena Arena;
|
||||
struct Arena
|
||||
{
|
||||
Arena *prev; // previous arena in chain
|
||||
Arena *current; // current arena in chain
|
||||
ArenaFlags flags;
|
||||
U64 cmt_size;
|
||||
U64 res_size;
|
||||
U64 base_pos;
|
||||
U64 pos;
|
||||
U64 cmt;
|
||||
U64 res;
|
||||
char *allocation_site_file;
|
||||
int allocation_site_line;
|
||||
char *name;
|
||||
#if ARENA_FREE_LIST
|
||||
Arena *free_last;
|
||||
#endif
|
||||
#if ARENA_TABLE_DEBUG
|
||||
struct ArenaTableNode *table_node;
|
||||
#endif
|
||||
};
|
||||
StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check);
|
||||
|
||||
typedef struct Temp Temp;
|
||||
struct Temp
|
||||
{
|
||||
Arena *arena;
|
||||
U64 pos;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arena Functions
|
||||
|
||||
global U64 arena_default_reserve_size = MB(64);
|
||||
global U64 arena_default_commit_size = KB(64);
|
||||
global ArenaFlags arena_default_flags = 0;
|
||||
|
||||
//- rjf: arena creation/destruction
|
||||
internal Arena *arena_alloc_(ArenaParams *params);
|
||||
#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = arena_default_reserve_size, .commit_size = arena_default_commit_size, .flags = arena_default_flags, .allocation_site_file = __FILE__, .allocation_site_line = __LINE__, __VA_ARGS__})
|
||||
internal void arena_release(Arena *arena);
|
||||
|
||||
//- rjf: arena push/pop/pos core functions
|
||||
internal void *arena_push(Arena *arena, U64 size, U64 align, B32 zero);
|
||||
internal U64 arena_pos(Arena *arena);
|
||||
internal void arena_pop_to(Arena *arena, U64 pos);
|
||||
|
||||
//- rjf: arena push/pop helpers
|
||||
internal void arena_clear(Arena *arena);
|
||||
internal void arena_pop(Arena *arena, U64 amt);
|
||||
|
||||
//- rjf: temporary arena scopes
|
||||
internal Temp temp_begin(Arena *arena);
|
||||
internal void temp_end(Temp temp);
|
||||
|
||||
//- rjf: push helper macros
|
||||
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (0))
|
||||
#define push_array_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (1))
|
||||
#define push_array_no_zero(a, T, c) push_array_no_zero_aligned(a, T, c, Max(8, AlignOf(T)))
|
||||
#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, AlignOf(T)))
|
||||
|
||||
#endif // BASE_ARENA_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_ARENA_H
|
||||
#define BASE_ARENA_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Constants
|
||||
|
||||
#define ARENA_HEADER_SIZE 128
|
||||
|
||||
#ifndef ARENA_RESERVE_SIZE
|
||||
# define ARENA_RESERVE_SIZE MB(64)
|
||||
#endif
|
||||
#ifndef ARENA_COMMIT_SIZE
|
||||
# define ARENA_COMMIT_SIZE KB(64)
|
||||
#endif
|
||||
|
||||
#ifndef ARENA_RESERVE_SIZE_LARGE_PAGES
|
||||
# define ARENA_RESERVE_SIZE_LARGE_PAGES MB(8)
|
||||
#endif
|
||||
#ifndef ARENA_COMMIT_SIZE_LARGE_PAGES
|
||||
# define ARENA_COMMIT_SIZE_LARGE_PAGES MB(2)
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arena Types
|
||||
|
||||
typedef struct Arena Arena;
|
||||
struct Arena
|
||||
{
|
||||
struct Arena *prev;
|
||||
struct Arena *current;
|
||||
U64 base_pos;
|
||||
U64 pos;
|
||||
U64 cmt;
|
||||
U64 res;
|
||||
U64 align;
|
||||
B8 grow;
|
||||
B8 large_pages;
|
||||
};
|
||||
|
||||
typedef struct Temp Temp;
|
||||
struct Temp
|
||||
{
|
||||
Arena *arena;
|
||||
U64 pos;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// Implementation
|
||||
|
||||
internal Arena* arena_alloc__sized(U64 init_res, U64 init_cmt);
|
||||
|
||||
internal Arena* arena_alloc(void);
|
||||
internal void arena_release(Arena *arena);
|
||||
|
||||
internal void* arena_push__impl(Arena *arena, U64 size);
|
||||
internal U64 arena_pos(Arena *arena);
|
||||
internal void arena_pop_to(Arena *arena, U64 pos);
|
||||
|
||||
internal void arena_absorb(Arena *arena, Arena *sub);
|
||||
|
||||
////////////////////////////////
|
||||
// Wrappers
|
||||
|
||||
internal void* arena_push(Arena *arena, U64 size);
|
||||
internal void* arena_push_contiguous(Arena *arena, U64 size);
|
||||
internal void arena_clear(Arena *arena);
|
||||
internal void arena_push_align(Arena *arena, U64 align);
|
||||
internal void arena_put_back(Arena *arena, U64 amt);
|
||||
|
||||
internal Temp temp_begin(Arena *arena);
|
||||
internal void temp_end(Temp temp);
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(allen): "Mini-Arena" Helper
|
||||
|
||||
internal B32 ensure_commit(void **cmt, void *pos, U64 cmt_block_size);
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(allen): Main API Macros
|
||||
|
||||
#define push_array_no_zero(a,T,c) (T*)arena_push((a), sizeof(T)*(c))
|
||||
#define push_array(a,T,c) (T*)MemoryZero(push_array_no_zero(a,T,c), sizeof(T)*(c))
|
||||
|
||||
#endif // BASE_ARENA_H
|
||||
|
||||
+232
-228
@@ -1,228 +1,232 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Command Line Parsing Functions
|
||||
|
||||
internal CmdLineOpt **
|
||||
cmd_line_slot_from_string(CmdLine *cmd_line, String8 string)
|
||||
{
|
||||
CmdLineOpt **slot = 0;
|
||||
if(cmd_line->option_table_size != 0)
|
||||
{
|
||||
U64 hash = u64_hash_from_str8(string);
|
||||
U64 bucket = hash % cmd_line->option_table_size;
|
||||
slot = &cmd_line->option_table[bucket];
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
internal CmdLineOpt *
|
||||
cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string)
|
||||
{
|
||||
CmdLineOpt *result = 0;
|
||||
for(CmdLineOpt *var = *slot; var; var = var->hash_next)
|
||||
{
|
||||
if(str8_match(string, var->string, 0))
|
||||
{
|
||||
result = var;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var)
|
||||
{
|
||||
SLLQueuePush(list->first, list->last, var);
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
internal CmdLineOpt *
|
||||
cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values)
|
||||
{
|
||||
CmdLineOpt *var = 0;
|
||||
CmdLineOpt **slot = cmd_line_slot_from_string(cmd_line, string);
|
||||
CmdLineOpt *existing_var = cmd_line_opt_from_slot(slot, string);
|
||||
if(existing_var != 0)
|
||||
{
|
||||
var = existing_var;
|
||||
}
|
||||
else
|
||||
{
|
||||
var = push_array(arena, CmdLineOpt, 1);
|
||||
var->hash_next = *slot;
|
||||
var->hash = u64_hash_from_str8(string);
|
||||
var->string = push_str8_copy(arena, string);
|
||||
var->value_strings = values;
|
||||
StringJoin join = {0};
|
||||
join.pre = str8_lit("");
|
||||
join.sep = str8_lit(",");
|
||||
join.post = str8_lit("");
|
||||
var->value_string = str8_list_join(arena, &var->value_strings, &join);
|
||||
*slot = var;
|
||||
cmd_line_push_opt(&cmd_line->options, var);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
internal CmdLine
|
||||
cmd_line_from_string_list(Arena *arena, String8List command_line)
|
||||
{
|
||||
CmdLine parsed = {0};
|
||||
parsed.exe_name = command_line.first->string;
|
||||
parsed.option_table_size = 64;
|
||||
parsed.option_table = push_array(arena, CmdLineOpt *, parsed.option_table_size);
|
||||
|
||||
//- rjf: parse options / inputs
|
||||
B32 after_passthrough_option = 0;
|
||||
B32 first_passthrough = 1;
|
||||
for(String8Node *node = command_line.first->next, *next = 0; node != 0; node = next)
|
||||
{
|
||||
next = node->next;
|
||||
|
||||
//- rjf: look at --, -, or / (only on Windows) at the start of an
|
||||
// argument to determine if it's a flag option. all arguments after a
|
||||
// single "--" (with no trailing string on the command line will be
|
||||
// considered as passthrough input strings.
|
||||
B32 is_option = 0;
|
||||
String8 option_name = node->string;
|
||||
if(!after_passthrough_option)
|
||||
{
|
||||
is_option = 1;
|
||||
if(str8_match(node->string, str8_lit("--"), 0))
|
||||
{
|
||||
after_passthrough_option = 1;
|
||||
is_option = 0;
|
||||
}
|
||||
else if(str8_match(str8_prefix(node->string, 2), str8_lit("--"), 0))
|
||||
{
|
||||
option_name = str8_skip(option_name, 2);
|
||||
}
|
||||
else if(str8_match(str8_prefix(node->string, 1), str8_lit("-"), 0))
|
||||
{
|
||||
option_name = str8_skip(option_name, 1);
|
||||
}
|
||||
else if(OperatingSystem_CURRENT == OperatingSystem_Windows &&
|
||||
str8_match(str8_prefix(node->string, 1), str8_lit("/"), 0))
|
||||
{
|
||||
option_name = str8_skip(option_name, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
is_option = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: string is an option
|
||||
if(is_option)
|
||||
{
|
||||
// rjf: unpack option prefix
|
||||
B32 has_values = 0;
|
||||
U64 value_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0);
|
||||
U64 value_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0);
|
||||
U64 value_signifier_position = Min(value_signifier_position1, value_signifier_position2);
|
||||
String8 value_portion_this_string = str8_skip(option_name, value_signifier_position+1);
|
||||
if(value_signifier_position < option_name.size)
|
||||
{
|
||||
has_values = 1;
|
||||
}
|
||||
option_name = str8_prefix(option_name, value_signifier_position);
|
||||
|
||||
// rjf: parse option's values
|
||||
String8List values = {0};
|
||||
if(has_values)
|
||||
{
|
||||
for(String8Node *n = node; n; n = n->next)
|
||||
{
|
||||
next = n->next;
|
||||
String8 string = n->string;
|
||||
if(n == node)
|
||||
{
|
||||
string = value_portion_this_string;
|
||||
}
|
||||
U8 splits[] = { ',' };
|
||||
String8List values_in_this_string = str8_split(arena, string, splits, ArrayCount(splits), 0);
|
||||
for(String8Node *sub_val = values_in_this_string.first; sub_val; sub_val = sub_val->next)
|
||||
{
|
||||
str8_list_push(arena, &values, sub_val->string);
|
||||
}
|
||||
if(!str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) &&
|
||||
(n != node || value_portion_this_string.size != 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: store
|
||||
cmd_line_insert_opt(arena, &parsed, option_name, values);
|
||||
}
|
||||
|
||||
//- rjf: default path - treat as a passthrough input
|
||||
else if(!str8_match(node->string, str8_lit("--"), 0) || !first_passthrough)
|
||||
{
|
||||
str8_list_push(arena, &parsed.inputs, node->string);
|
||||
first_passthrough = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: fill argc/argv
|
||||
parsed.argc = command_line.node_count;
|
||||
parsed.argv = push_array(arena, char *, parsed.argc);
|
||||
{
|
||||
U64 idx = 0;
|
||||
for(String8Node *n = command_line.first; n != 0; n = n->next)
|
||||
{
|
||||
parsed.argv[idx] = (char *)push_str8_copy(arena, n->string).str;
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
internal CmdLineOpt *
|
||||
cmd_line_opt_from_string(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
return cmd_line_opt_from_slot(cmd_line_slot_from_string(cmd_line, name), name);
|
||||
}
|
||||
|
||||
internal String8List
|
||||
cmd_line_strings(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
String8List result = {0};
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
if(var != 0)
|
||||
{
|
||||
result = var->value_strings;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
cmd_line_string(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
String8 result = {0};
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
if(var != 0)
|
||||
{
|
||||
result = var->value_string;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
cmd_line_has_flag(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
return (var != 0);
|
||||
}
|
||||
|
||||
internal B32
|
||||
cmd_line_has_argument(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
return (var != 0 && var->value_strings.node_count > 0);
|
||||
}
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(rjf): Command Line Option Parsing
|
||||
|
||||
internal U64
|
||||
cmd_line_hash_from_string(String8 string)
|
||||
{
|
||||
U64 result = 5381;
|
||||
for(U64 i = 0; i < string.size; i += 1)
|
||||
{
|
||||
result = ((result << 5) + result) + string.str[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CmdLineOpt **
|
||||
cmd_line_slot_from_string(CmdLine *cmd_line, String8 string)
|
||||
{
|
||||
CmdLineOpt **slot = 0;
|
||||
if(cmd_line->option_table_size != 0)
|
||||
{
|
||||
U64 hash = cmd_line_hash_from_string(string);
|
||||
U64 bucket = hash % cmd_line->option_table_size;
|
||||
slot = &cmd_line->option_table[bucket];
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
internal CmdLineOpt *
|
||||
cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string)
|
||||
{
|
||||
CmdLineOpt *result = 0;
|
||||
for(CmdLineOpt *var = *slot; var; var = var->hash_next)
|
||||
{
|
||||
if(str8_match(string, var->string, 0))
|
||||
{
|
||||
result = var;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var)
|
||||
{
|
||||
SLLQueuePush(list->first, list->last, var);
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
internal CmdLineOpt *
|
||||
cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values)
|
||||
{
|
||||
CmdLineOpt *var = 0;
|
||||
CmdLineOpt **slot = cmd_line_slot_from_string(cmd_line, string);
|
||||
CmdLineOpt *existing_var = cmd_line_opt_from_slot(slot, string);
|
||||
if(existing_var != 0)
|
||||
{
|
||||
var = existing_var;
|
||||
}
|
||||
else
|
||||
{
|
||||
var = push_array(arena, CmdLineOpt, 1);
|
||||
var->hash_next = *slot;
|
||||
var->hash = cmd_line_hash_from_string(string);
|
||||
var->string = push_str8_copy(arena, string);
|
||||
var->value_strings = values;
|
||||
StringJoin join = {0};
|
||||
join.pre = str8_lit("");
|
||||
join.sep = str8_lit(",");
|
||||
join.post = str8_lit("");
|
||||
var->value_string = str8_list_join(arena, &var->value_strings, &join);
|
||||
*slot = var;
|
||||
cmd_line_push_opt(&cmd_line->options, var);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
internal CmdLine
|
||||
cmd_line_from_string_list(Arena *arena, String8List command_line)
|
||||
{
|
||||
CmdLine parsed = {0};
|
||||
parsed.exe_name = command_line.first->string;
|
||||
|
||||
// NOTE(rjf): Set up config option table.
|
||||
{
|
||||
parsed.option_table_size = 4096;
|
||||
parsed.option_table = push_array(arena, CmdLineOpt *, parsed.option_table_size);
|
||||
}
|
||||
|
||||
// NOTE(rjf): Parse command line.
|
||||
B32 after_passthrough_option = 0;
|
||||
B32 first_passthrough = 1;
|
||||
for(String8Node *node = command_line.first->next, *next = 0; node != 0; node = next)
|
||||
{
|
||||
next = node->next;
|
||||
String8 option_name = node->string;
|
||||
|
||||
// NOTE(rjf): Look at -- or - at the start of an argument to determine if it's
|
||||
// a flag option. All arguments after a single "--" (with no trailing string
|
||||
// on the command line will be considered as input files.
|
||||
B32 is_option = 1;
|
||||
if(after_passthrough_option == 0)
|
||||
{
|
||||
if(str8_match(node->string, str8_lit("--"), 0))
|
||||
{
|
||||
after_passthrough_option = 1;
|
||||
is_option = 0;
|
||||
}
|
||||
else if(str8_match(str8_prefix(node->string, 2), str8_lit("--"), 0))
|
||||
{
|
||||
option_name = str8_skip(option_name, 2);
|
||||
}
|
||||
else if(str8_match(str8_prefix(node->string, 1), str8_lit("-"), 0))
|
||||
{
|
||||
option_name = str8_skip(option_name, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
is_option = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
is_option = 0;
|
||||
}
|
||||
|
||||
// NOTE(rjf): This string is an option.
|
||||
if(is_option)
|
||||
{
|
||||
B32 has_arguments = 0;
|
||||
U64 arg_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0);
|
||||
U64 arg_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0);
|
||||
U64 arg_signifier_position = Min(arg_signifier_position1, arg_signifier_position2);
|
||||
String8 arg_portion_this_string = str8_skip(option_name, arg_signifier_position+1);
|
||||
if(arg_signifier_position < option_name.size)
|
||||
{
|
||||
has_arguments = 1;
|
||||
}
|
||||
option_name = str8_prefix(option_name, arg_signifier_position);
|
||||
|
||||
String8List arguments = {0};
|
||||
|
||||
// NOTE(rjf): Parse arguments.
|
||||
if(has_arguments)
|
||||
{
|
||||
for(String8Node *n = node; n; n = n->next)
|
||||
{
|
||||
next = n->next;
|
||||
|
||||
String8 string = n->string;
|
||||
if(n == node)
|
||||
{
|
||||
string = arg_portion_this_string;
|
||||
}
|
||||
|
||||
U8 splits[] = { ',' };
|
||||
String8List args_in_this_string = str8_split(arena, string, splits, ArrayCount(splits), 0);
|
||||
for(String8Node *sub_arg = args_in_this_string.first; sub_arg; sub_arg = sub_arg->next)
|
||||
{
|
||||
str8_list_push(arena, &arguments, sub_arg->string);
|
||||
}
|
||||
if(!str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) &&
|
||||
(n != node || arg_portion_this_string.size != 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(rjf): Register config variable.
|
||||
cmd_line_insert_opt(arena, &parsed, option_name, arguments);
|
||||
}
|
||||
|
||||
// NOTE(rjf): Default path, treat as a passthrough config option to be
|
||||
// handled by tool-specific code.
|
||||
else if(!str8_match(node->string, str8_lit("--"), 0) || !first_passthrough)
|
||||
{
|
||||
str8_list_push(arena, &parsed.inputs, node->string);
|
||||
after_passthrough_option = 1;
|
||||
first_passthrough = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
internal CmdLineOpt *
|
||||
cmd_line_opt_from_string(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
return cmd_line_opt_from_slot(cmd_line_slot_from_string(cmd_line, name), name);
|
||||
}
|
||||
|
||||
internal String8List
|
||||
cmd_line_strings(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
String8List result = {0};
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
if(var != 0)
|
||||
{
|
||||
result = var->value_strings;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
cmd_line_string(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
String8 result = {0};
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
if(var != 0)
|
||||
{
|
||||
result = var->value_string;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
cmd_line_has_flag(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
return(var != 0);
|
||||
}
|
||||
|
||||
internal B32
|
||||
cmd_line_has_argument(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
return(var != 0 && var->value_strings.node_count > 0);
|
||||
}
|
||||
|
||||
@@ -1,55 +1,54 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_COMMAND_LINE_H
|
||||
#define BASE_COMMAND_LINE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parsed Command Line Types
|
||||
|
||||
typedef struct CmdLineOpt CmdLineOpt;
|
||||
struct CmdLineOpt
|
||||
{
|
||||
CmdLineOpt *next;
|
||||
CmdLineOpt *hash_next;
|
||||
U64 hash;
|
||||
String8 string;
|
||||
String8List value_strings;
|
||||
String8 value_string;
|
||||
};
|
||||
|
||||
typedef struct CmdLineOptList CmdLineOptList;
|
||||
struct CmdLineOptList
|
||||
{
|
||||
U64 count;
|
||||
CmdLineOpt *first;
|
||||
CmdLineOpt *last;
|
||||
};
|
||||
|
||||
typedef struct CmdLine CmdLine;
|
||||
struct CmdLine
|
||||
{
|
||||
String8 exe_name;
|
||||
CmdLineOptList options;
|
||||
String8List inputs;
|
||||
U64 option_table_size;
|
||||
CmdLineOpt **option_table;
|
||||
U64 argc;
|
||||
char **argv;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Command Line Parsing Functions
|
||||
|
||||
internal CmdLineOpt** cmd_line_slot_from_string(CmdLine *cmd_line, String8 string);
|
||||
internal CmdLineOpt* cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string);
|
||||
internal void cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var);
|
||||
internal CmdLineOpt* cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values);
|
||||
internal CmdLine cmd_line_from_string_list(Arena *arena, String8List arguments);
|
||||
internal CmdLineOpt* cmd_line_opt_from_string(CmdLine *cmd_line, String8 name);
|
||||
internal String8List cmd_line_strings(CmdLine *cmd_line, String8 name);
|
||||
internal String8 cmd_line_string(CmdLine *cmd_line, String8 name);
|
||||
internal B32 cmd_line_has_flag(CmdLine *cmd_line, String8 name);
|
||||
internal B32 cmd_line_has_argument(CmdLine *cmd_line, String8 name);
|
||||
|
||||
#endif // BASE_COMMAND_LINE_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_COMMAND_LINE_H
|
||||
#define BASE_COMMAND_LINE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parsed Command Line Types
|
||||
|
||||
typedef struct CmdLineOpt CmdLineOpt;
|
||||
struct CmdLineOpt
|
||||
{
|
||||
CmdLineOpt *next;
|
||||
CmdLineOpt *hash_next;
|
||||
U64 hash;
|
||||
String8 string;
|
||||
String8List value_strings;
|
||||
String8 value_string;
|
||||
};
|
||||
|
||||
typedef struct CmdLineOptList CmdLineOptList;
|
||||
struct CmdLineOptList
|
||||
{
|
||||
U64 count;
|
||||
CmdLineOpt *first;
|
||||
CmdLineOpt *last;
|
||||
};
|
||||
|
||||
typedef struct CmdLine CmdLine;
|
||||
struct CmdLine
|
||||
{
|
||||
String8 exe_name;
|
||||
CmdLineOptList options;
|
||||
String8List inputs;
|
||||
U64 option_table_size;
|
||||
CmdLineOpt **option_table;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(rjf): Command Line Option Parsing
|
||||
|
||||
internal U64 cmd_line_hash_from_string(String8 string);
|
||||
internal CmdLineOpt** cmd_line_slot_from_string(CmdLine *cmd_line, String8 string);
|
||||
internal CmdLineOpt* cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string);
|
||||
internal void cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var);
|
||||
internal CmdLineOpt* cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values);
|
||||
internal CmdLine cmd_line_from_string_list(Arena *arena, String8List arguments);
|
||||
internal CmdLineOpt* cmd_line_opt_from_string(CmdLine *cmd_line, String8 name);
|
||||
internal String8List cmd_line_strings(CmdLine *cmd_line, String8 name);
|
||||
internal String8 cmd_line_string(CmdLine *cmd_line, String8 name);
|
||||
internal B32 cmd_line_has_flag(CmdLine *cmd_line, String8 name);
|
||||
internal B32 cmd_line_has_argument(CmdLine *cmd_line, String8 name);
|
||||
|
||||
#endif // BASE_COMMAND_LINE_H
|
||||
|
||||
+247
-295
@@ -1,295 +1,247 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_CONTEXT_CRACKING_H
|
||||
#define BASE_CONTEXT_CRACKING_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Clang OS/Arch Cracking
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
# define COMPILER_CLANG 1
|
||||
|
||||
# if defined(_WIN32)
|
||||
# define OS_WINDOWS 1
|
||||
# elif defined(__gnu_linux__) || defined(__linux__)
|
||||
# define OS_LINUX 1
|
||||
# elif defined(__APPLE__) && defined(__MACH__)
|
||||
# define OS_MAC 1
|
||||
# else
|
||||
# error This compiler/OS combo is not supported.
|
||||
# endif
|
||||
|
||||
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
|
||||
# define ARCH_X64 1
|
||||
# elif defined(i386) || defined(__i386) || defined(__i386__)
|
||||
# define ARCH_X86 1
|
||||
# elif defined(__aarch64__)
|
||||
# define ARCH_ARM64 1
|
||||
# elif defined(__arm__)
|
||||
# define ARCH_ARM32 1
|
||||
# else
|
||||
# error Architecture not supported.
|
||||
# endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: MSVC OS/Arch Cracking
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
# define COMPILER_MSVC 1
|
||||
|
||||
# if _MSC_VER >= 1920
|
||||
# define COMPILER_MSVC_YEAR 2019
|
||||
# elif _MSC_VER >= 1910
|
||||
# define COMPILER_MSVC_YEAR 2017
|
||||
# elif _MSC_VER >= 1900
|
||||
# define COMPILER_MSVC_YEAR 2015
|
||||
# elif _MSC_VER >= 1800
|
||||
# define COMPILER_MSVC_YEAR 2013
|
||||
# elif _MSC_VER >= 1700
|
||||
# define COMPILER_MSVC_YEAR 2012
|
||||
# elif _MSC_VER >= 1600
|
||||
# define COMPILER_MSVC_YEAR 2010
|
||||
# elif _MSC_VER >= 1500
|
||||
# define COMPILER_MSVC_YEAR 2008
|
||||
# elif _MSC_VER >= 1400
|
||||
# define COMPILER_MSVC_YEAR 2005
|
||||
# else
|
||||
# define COMPILER_MSVC_YEAR 0
|
||||
# endif
|
||||
|
||||
# if defined(_WIN32)
|
||||
# define OS_WINDOWS 1
|
||||
# else
|
||||
# error This compiler/OS combo is not supported.
|
||||
# endif
|
||||
|
||||
# if defined(_M_AMD64)
|
||||
# define ARCH_X64 1
|
||||
# elif defined(_M_IX86)
|
||||
# define ARCH_X86 1
|
||||
# elif defined(_M_ARM64)
|
||||
# define ARCH_ARM64 1
|
||||
# elif defined(_M_ARM)
|
||||
# define ARCH_ARM32 1
|
||||
# else
|
||||
# error Architecture not supported.
|
||||
# endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: GCC OS/Arch Cracking
|
||||
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
|
||||
# define COMPILER_GCC 1
|
||||
|
||||
# if defined(__gnu_linux__) || defined(__linux__)
|
||||
# define OS_LINUX 1
|
||||
# else
|
||||
# error This compiler/OS combo is not supported.
|
||||
# endif
|
||||
|
||||
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
|
||||
# define ARCH_X64 1
|
||||
# elif defined(i386) || defined(__i386) || defined(__i386__)
|
||||
# define ARCH_X86 1
|
||||
# elif defined(__aarch64__)
|
||||
# define ARCH_ARM64 1
|
||||
# elif defined(__arm__)
|
||||
# define ARCH_ARM32 1
|
||||
# else
|
||||
# error Architecture not supported.
|
||||
# endif
|
||||
|
||||
#else
|
||||
# error Compiler not supported.
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arch Cracking
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
# define ARCH_64BIT 1
|
||||
#elif defined(ARCH_X86)
|
||||
# define ARCH_32BIT 1
|
||||
#endif
|
||||
|
||||
#if ARCH_ARM32 || ARCH_ARM64 || ARCH_X64 || ARCH_X86
|
||||
# define ARCH_LITTLE_ENDIAN 1
|
||||
#else
|
||||
# error Endianness of this architecture not understood by context cracker.
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Language Cracking
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# define LANG_CPP 1
|
||||
#else
|
||||
# define LANG_C 1
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Build Option Cracking
|
||||
|
||||
#if !defined(BUILD_DEBUG)
|
||||
# define BUILD_DEBUG 1
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_TESTS)
|
||||
# define BUILD_TESTS 0
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_SUPPLEMENTARY_UNIT)
|
||||
# define BUILD_SUPPLEMENTARY_UNIT 0
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_ENTRY_DEFINING_UNIT)
|
||||
# define BUILD_ENTRY_DEFINING_UNIT 1
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_CONSOLE_INTERFACE)
|
||||
# define BUILD_CONSOLE_INTERFACE 0
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_VERSION_MAJOR)
|
||||
# define BUILD_VERSION_MAJOR 0
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_VERSION_MINOR)
|
||||
# define BUILD_VERSION_MINOR 9
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_VERSION_PATCH)
|
||||
# define BUILD_VERSION_PATCH 28
|
||||
#endif
|
||||
|
||||
#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH)
|
||||
#if BUILD_DEBUG
|
||||
# define BUILD_MODE_STRING_LITERAL_APPEND " [Debug]"
|
||||
#else
|
||||
# define BUILD_MODE_STRING_LITERAL_APPEND ""
|
||||
#endif
|
||||
#if defined(BUILD_GIT_HASH)
|
||||
# define BUILD_GIT_HASH_STRING_LITERAL_APPEND " [" BUILD_GIT_HASH "]"
|
||||
#else
|
||||
# define BUILD_GIT_HASH_STRING_LITERAL_APPEND ""
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_TITLE)
|
||||
# define BUILD_TITLE "Untitled"
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_RELEASE_PHASE_STRING_LITERAL)
|
||||
# define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA"
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_ISSUES_LINK_STRING_LITERAL)
|
||||
# define BUILD_ISSUES_LINK_STRING_LITERAL "https://github.com/EpicGames/raddebugger/issues"
|
||||
#endif
|
||||
|
||||
#define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Zero All Undefined Options
|
||||
|
||||
#if !defined(ARCH_32BIT)
|
||||
# define ARCH_32BIT 0
|
||||
#endif
|
||||
#if !defined(ARCH_64BIT)
|
||||
# define ARCH_64BIT 0
|
||||
#endif
|
||||
#if !defined(ARCH_X64)
|
||||
# define ARCH_X64 0
|
||||
#endif
|
||||
#if !defined(ARCH_X86)
|
||||
# define ARCH_X86 0
|
||||
#endif
|
||||
#if !defined(ARCH_ARM64)
|
||||
# define ARCH_ARM64 0
|
||||
#endif
|
||||
#if !defined(ARCH_ARM32)
|
||||
# define ARCH_ARM32 0
|
||||
#endif
|
||||
#if !defined(COMPILER_MSVC)
|
||||
# define COMPILER_MSVC 0
|
||||
#endif
|
||||
#if !defined(COMPILER_GCC)
|
||||
# define COMPILER_GCC 0
|
||||
#endif
|
||||
#if !defined(COMPILER_CLANG)
|
||||
# define COMPILER_CLANG 0
|
||||
#endif
|
||||
#if !defined(OS_WINDOWS)
|
||||
# define OS_WINDOWS 0
|
||||
#endif
|
||||
#if !defined(OS_LINUX)
|
||||
# define OS_LINUX 0
|
||||
#endif
|
||||
#if !defined(OS_MAC)
|
||||
# define OS_MAC 0
|
||||
#endif
|
||||
#if !defined(LANG_CPP)
|
||||
# define LANG_CPP 0
|
||||
#endif
|
||||
#if !defined(LANG_C)
|
||||
# define LANG_C 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Unsupported Errors
|
||||
|
||||
#if ARCH_X86
|
||||
# error You tried to build in x86 (32 bit) mode, but currently, only building in x64 (64 bit) mode is supported.
|
||||
#endif
|
||||
#if !ARCH_X64
|
||||
# error You tried to build with an unsupported architecture. Currently, only building in x64 mode is supported.
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
// extra intrinisc includes, so older clang versions are happy
|
||||
// when optiona intrinsics like avx2 & avx512 are used later
|
||||
// these must come first before any other intrinsic includes
|
||||
// otherwise compiler will define macros that prevent declarations
|
||||
// of intrinsics when included next time
|
||||
|
||||
#if ARCH_X64 && COMPILER_CLANG
|
||||
# if defined(__IMMINTRIN_H)
|
||||
# error "include this header before immintrin.h / x86intrin.h / intrin.h"
|
||||
# endif
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wreserved-macro-identifier"
|
||||
# pragma push_macro("__SHA__")
|
||||
# pragma push_macro("__AVX__")
|
||||
# pragma push_macro("__AVX2__")
|
||||
# pragma push_macro("__BMI2__")
|
||||
# pragma push_macro("__SSE4_1__")
|
||||
# pragma push_macro("__AVX512F__")
|
||||
# pragma push_macro("__AVX512VL__")
|
||||
# pragma push_macro("__AVX512BW__")
|
||||
# pragma push_macro("__AVX512VBMI__")
|
||||
# define __SHA__ 1
|
||||
# define __AVX__ 1
|
||||
# define __AVX2__ 1
|
||||
# define __BMI2__ 1
|
||||
# define __SSE4_1__ 1
|
||||
# define __AVX512F__ 1
|
||||
# define __AVX512VL__ 1
|
||||
# define __AVX512BW__ 1
|
||||
# define __AVX512VBMI__ 1
|
||||
# include <immintrin.h>
|
||||
# pragma pop_macro("__AVX512VBMI__")
|
||||
# pragma pop_macro("__AVX512BW__")
|
||||
# pragma pop_macro("__AVX512VL__")
|
||||
# pragma pop_macro("__AVX512F__")
|
||||
# pragma pop_macro("__SSE4_1__")
|
||||
# pragma pop_macro("__BMI2__")
|
||||
# pragma pop_macro("__AVX2__")
|
||||
# pragma pop_macro("__AVX__")
|
||||
# pragma pop_macro("__SHA__")
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // BASE_CONTEXT_CRACKING_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_CONTEXT_CRACKING_H
|
||||
#define BASE_CONTEXT_CRACKING_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Clang OS/Arch Cracking
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
# define COMPILER_CLANG 1
|
||||
|
||||
# if defined(_WIN32)
|
||||
# define OS_WINDOWS 1
|
||||
# elif defined(__gnu_linux__) || defined(__linux__)
|
||||
# define OS_LINUX 1
|
||||
# elif defined(__APPLE__) && defined(__MACH__)
|
||||
# define OS_MAC 1
|
||||
# else
|
||||
# error This compiler/OS combo is not supported.
|
||||
# endif
|
||||
|
||||
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
|
||||
# define ARCH_X64 1
|
||||
# elif defined(i386) || defined(__i386) || defined(__i386__)
|
||||
# define ARCH_X86 1
|
||||
# elif defined(__aarch64__)
|
||||
# define ARCH_ARM64 1
|
||||
# elif defined(__arm__)
|
||||
# define ARCH_ARM32 1
|
||||
# else
|
||||
# error Architecture not supported.
|
||||
# endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: MSVC OS/Arch Cracking
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
# define COMPILER_MSVC 1
|
||||
|
||||
# if _MSC_VER >= 1920
|
||||
# define COMPILER_MSVC_YEAR 2019
|
||||
# elif _MSC_VER >= 1910
|
||||
# define COMPILER_MSVC_YEAR 2017
|
||||
# elif _MSC_VER >= 1900
|
||||
# define COMPILER_MSVC_YEAR 2015
|
||||
# elif _MSC_VER >= 1800
|
||||
# define COMPILER_MSVC_YEAR 2013
|
||||
# elif _MSC_VER >= 1700
|
||||
# define COMPILER_MSVC_YEAR 2012
|
||||
# elif _MSC_VER >= 1600
|
||||
# define COMPILER_MSVC_YEAR 2010
|
||||
# elif _MSC_VER >= 1500
|
||||
# define COMPILER_MSVC_YEAR 2008
|
||||
# elif _MSC_VER >= 1400
|
||||
# define COMPILER_MSVC_YEAR 2005
|
||||
# else
|
||||
# define COMPILER_MSVC_YEAR 0
|
||||
# endif
|
||||
|
||||
# if defined(_WIN32)
|
||||
# define OS_WINDOWS 1
|
||||
# else
|
||||
# error This compiler/OS combo is not supported.
|
||||
# endif
|
||||
|
||||
# if defined(_M_AMD64)
|
||||
# define ARCH_X64 1
|
||||
# elif defined(_M_IX86)
|
||||
# define ARCH_X86 1
|
||||
# elif defined(_M_ARM64)
|
||||
# define ARCH_ARM64 1
|
||||
# elif defined(_M_ARM)
|
||||
# define ARCH_ARM32 1
|
||||
# else
|
||||
# error Architecture not supported.
|
||||
# endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: GCC OS/Arch Cracking
|
||||
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
|
||||
# define COMPILER_GCC 1
|
||||
|
||||
# if defined(__gnu_linux__) || defined(__linux__)
|
||||
# define OS_LINUX 1
|
||||
# else
|
||||
# error This compiler/OS combo is not supported.
|
||||
# endif
|
||||
|
||||
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
|
||||
# define ARCH_X64 1
|
||||
# elif defined(i386) || defined(__i386) || defined(__i386__)
|
||||
# define ARCH_X86 1
|
||||
# elif defined(__aarch64__)
|
||||
# define ARCH_ARM64 1
|
||||
# elif defined(__arm__)
|
||||
# define ARCH_ARM32 1
|
||||
# else
|
||||
# error Architecture not supported.
|
||||
# endif
|
||||
|
||||
#else
|
||||
# error Compiler not supported.
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arch Cracking
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
# define ARCH_64BIT 1
|
||||
#elif defined(ARCH_X86)
|
||||
# define ARCH_32BIT 1
|
||||
#endif
|
||||
|
||||
#if ARCH_ARM32 || ARCH_ARM64 || ARCH_X64 || ARCH_X86
|
||||
# define ARCH_LITTLE_ENDIAN 1
|
||||
#else
|
||||
# error Endianness of this architecture not understood by context cracker.
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Language Cracking
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# define LANG_CPP 1
|
||||
#else
|
||||
# define LANG_C 1
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Build Option Cracking
|
||||
|
||||
#if !defined(BUILD_DEBUG)
|
||||
# define BUILD_DEBUG 1
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_SUPPLEMENTARY_UNIT)
|
||||
# define BUILD_SUPPLEMENTARY_UNIT 0
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_ENTRY_DEFINING_UNIT)
|
||||
# define BUILD_ENTRY_DEFINING_UNIT 1
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_CONSOLE_INTERFACE)
|
||||
# define BUILD_CONSOLE_INTERFACE 0
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_VERSION_MAJOR)
|
||||
# define BUILD_VERSION_MAJOR 0
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_VERSION_MINOR)
|
||||
# define BUILD_VERSION_MINOR 0
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_VERSION_PATCH)
|
||||
# define BUILD_VERSION_PATCH 0
|
||||
#endif
|
||||
|
||||
#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH)
|
||||
#if BUILD_DEBUG
|
||||
# define BUILD_MODE_STRING_LITERAL_APPEND " [Debug]"
|
||||
#else
|
||||
# define BUILD_MODE_STRING_LITERAL_APPEND ""
|
||||
#endif
|
||||
#if defined(BUILD_GIT_HASH)
|
||||
# define BUILD_GIT_HASH_STRING_LITERAL_APPEND " [" BUILD_GIT_HASH "]"
|
||||
#else
|
||||
# define BUILD_GIT_HASH_STRING_LITERAL_APPEND ""
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_TITLE)
|
||||
# define BUILD_TITLE "Untitled"
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_RELEASE_PHASE_STRING_LITERAL)
|
||||
# define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA"
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_ISSUES_LINK_STRING_LITERAL)
|
||||
# define BUILD_ISSUES_LINK_STRING_LITERAL "https://github.com/EpicGames/raddebugger/issues"
|
||||
#endif
|
||||
|
||||
#define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Zero All Undefined Options
|
||||
|
||||
#if !defined(ARCH_32BIT)
|
||||
# define ARCH_32BIT 0
|
||||
#endif
|
||||
#if !defined(ARCH_64BIT)
|
||||
# define ARCH_64BIT 0
|
||||
#endif
|
||||
#if !defined(ARCH_X64)
|
||||
# define ARCH_X64 0
|
||||
#endif
|
||||
#if !defined(ARCH_X86)
|
||||
# define ARCH_X86 0
|
||||
#endif
|
||||
#if !defined(ARCH_ARM64)
|
||||
# define ARCH_ARM64 0
|
||||
#endif
|
||||
#if !defined(ARCH_ARM32)
|
||||
# define ARCH_ARM32 0
|
||||
#endif
|
||||
#if !defined(COMPILER_MSVC)
|
||||
# define COMPILER_MSVC 0
|
||||
#endif
|
||||
#if !defined(COMPILER_GCC)
|
||||
# define COMPILER_GCC 0
|
||||
#endif
|
||||
#if !defined(COMPILER_CLANG)
|
||||
# define COMPILER_CLANG 0
|
||||
#endif
|
||||
#if !defined(OS_WINDOWS)
|
||||
# define OS_WINDOWS 0
|
||||
#endif
|
||||
#if !defined(OS_LINUX)
|
||||
# define OS_LINUX 0
|
||||
#endif
|
||||
#if !defined(OS_MAC)
|
||||
# define OS_MAC 0
|
||||
#endif
|
||||
#if !defined(LANG_CPP)
|
||||
# define LANG_CPP 0
|
||||
#endif
|
||||
#if !defined(LANG_C)
|
||||
# define LANG_C 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Unsupported Errors
|
||||
|
||||
#if ARCH_X86
|
||||
# error You tried to build in x86 (32 bit) mode, but currently, only building in x64 (64 bit) mode is supported.
|
||||
#endif
|
||||
#if !ARCH_X64
|
||||
# error You tried to build with an unsupported architecture. Currently, only building in x64 mode is supported.
|
||||
#endif
|
||||
|
||||
#endif // BASE_CONTEXT_CRACKING_H
|
||||
|
||||
+562
-809
File diff suppressed because it is too large
Load Diff
+782
-1135
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_DYNAMIC_LIBARIES_H
|
||||
#define BASE_DYNAMIC_LIBARIES_H
|
||||
|
||||
typedef struct Library Library;
|
||||
struct Library
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Dynamically-Loaded Libraries
|
||||
|
||||
internal Library library_open(String8 path);
|
||||
internal void library_close(Library lib);
|
||||
internal VoidProc *library_load_proc(Library lib, String8 name);
|
||||
|
||||
#endif // BASE_DYNAMIC_LIBARIES_H
|
||||
+95
-249
@@ -1,249 +1,95 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
global U64 global_update_tick_idx = 0;
|
||||
global U64 async_threads_count = 0;
|
||||
global CondVar async_tick_start_cond_var = {0};
|
||||
global Mutex async_tick_start_mutex = {0};
|
||||
global Mutex async_tick_stop_mutex = {0};
|
||||
global B32 async_loop_again = 0;
|
||||
global B32 async_loop_again_high_priority = 0;
|
||||
global B32 global_async_exit = 0;
|
||||
thread_static B32 is_async_thread = 0;
|
||||
|
||||
// NOTE(rjf): defined by target
|
||||
raddbg_entry_point(entry_point);
|
||||
internal void entry_point(CmdLine *cmdline);
|
||||
|
||||
internal void
|
||||
main_thread_base_entry_point(int arguments_count, char **arguments)
|
||||
{
|
||||
ThreadNameF("main_thread");
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
//- rjf: set up async thread group info
|
||||
async_tick_start_cond_var = cond_var_alloc();
|
||||
async_tick_start_mutex = mutex_alloc();
|
||||
async_tick_stop_mutex = mutex_alloc();
|
||||
|
||||
//- rjf: set up telemetry
|
||||
#if PROFILE_TELEMETRY
|
||||
local_persist char tm_data[MB(64)];
|
||||
tmLoadLibrary(TM_RELEASE);
|
||||
tmSetMaxThreadCount(256);
|
||||
tmInitialize(sizeof(tm_data), tm_data);
|
||||
#endif
|
||||
|
||||
//- rjf: set up spall
|
||||
#if PROFILE_SPALL
|
||||
spall_profile = spall_init_file_ex("spall_capture", 1, 0);
|
||||
#endif
|
||||
|
||||
//- rjf: parse command line
|
||||
String8List command_line_argument_strings = {0};
|
||||
{
|
||||
for EachIndex(idx, arguments_count)
|
||||
{
|
||||
str8_list_push(scratch.arena, &command_line_argument_strings, str8_cstring(arguments[idx]));
|
||||
}
|
||||
}
|
||||
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
|
||||
|
||||
//- rjf: begin captures
|
||||
B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture"));
|
||||
if(capture)
|
||||
{
|
||||
ProfBeginCapture(arguments[0]);
|
||||
ProfMsg(BUILD_TITLE);
|
||||
}
|
||||
|
||||
//- rjf: initialize all included layers
|
||||
#if defined(HTTP_H) && !defined(HTTP_INIT_MANUAL)
|
||||
http_init();
|
||||
#endif
|
||||
#if defined(SYMBOL_SERVER_H) && !defined(SMSV_INIT_MANUAL)
|
||||
smsv_init();
|
||||
#endif
|
||||
#if defined(ARTIFACT_CACHE_H) && !defined(AC_INIT_MANUAL)
|
||||
ac_init();
|
||||
#endif
|
||||
#if defined(CONTENT_H) && !defined(C_INIT_MANUAL)
|
||||
c_init();
|
||||
#endif
|
||||
#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL)
|
||||
fs_init();
|
||||
#endif
|
||||
#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL)
|
||||
mtx_init();
|
||||
#endif
|
||||
#if defined(DBG_INFO_H) && !defined(DI_INIT_MANUAL)
|
||||
di_init(&cmdline);
|
||||
#endif
|
||||
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
|
||||
dmn_init();
|
||||
#endif
|
||||
#if defined(DBG_ENGINE_CORE_H) && !defined(D_INIT_MANUAL)
|
||||
d_init();
|
||||
#endif
|
||||
#if defined(WINDOW_MANAGER_H) && !defined(WM_INIT_MANUAL)
|
||||
wm_init();
|
||||
#endif
|
||||
#if defined(FONT_PROVIDER_H) && !defined(FP_INIT_MANUAL)
|
||||
fp_init();
|
||||
#endif
|
||||
#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL)
|
||||
r_init(&cmdline);
|
||||
#endif
|
||||
#if defined(FONT_CACHE_H) && !defined(FNT_INIT_MANUAL)
|
||||
fnt_init();
|
||||
#endif
|
||||
#if defined(RADDBG_CORE_H) && !defined(RD_INIT_MANUAL)
|
||||
rd_init(&cmdline);
|
||||
#endif
|
||||
|
||||
//- rjf: launch async threads
|
||||
#if NEED_ASYNC
|
||||
Thread *async_threads = 0;
|
||||
U64 lane_broadcast_val = 0;
|
||||
{
|
||||
U64 num_main_threads = 1;
|
||||
#if defined(DBG_ENGINE_CORE_H)
|
||||
num_main_threads += 1;
|
||||
#endif
|
||||
U64 num_async_threads = get_system_info()->logical_processor_count;
|
||||
U64 num_main_threads_clamped = Min(num_async_threads, num_main_threads);
|
||||
num_async_threads -= num_main_threads_clamped;
|
||||
String8 num_async_threads_string = cmd_line_string(&cmdline, str8_lit("async_thread_count"));
|
||||
if(num_async_threads_string.size != 0)
|
||||
{
|
||||
try_u64_from_str8_c_rules(num_async_threads_string, &num_async_threads);
|
||||
}
|
||||
num_async_threads = Max(1, num_async_threads);
|
||||
Barrier barrier = barrier_alloc(num_async_threads);
|
||||
LaneCtx *lane_ctxs = push_array(scratch.arena, LaneCtx, num_async_threads);
|
||||
async_threads_count = num_async_threads;
|
||||
async_threads = push_array(scratch.arena, Thread, async_threads_count);
|
||||
for EachIndex(idx, num_async_threads)
|
||||
{
|
||||
lane_ctxs[idx].lane_idx = idx;
|
||||
lane_ctxs[idx].lane_count = async_threads_count;
|
||||
lane_ctxs[idx].barrier = barrier;
|
||||
lane_ctxs[idx].broadcast_memory = &lane_broadcast_val;
|
||||
async_threads[idx] = thread_launch(async_thread_entry_point, &lane_ctxs[idx]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//- rjf: call into entry point
|
||||
entry_point(&cmdline);
|
||||
|
||||
//- rjf: join async threads
|
||||
#if NEED_ASYNC
|
||||
ins_atomic_u32_inc_eval(&global_async_exit);
|
||||
ins_atomic_u32_inc_eval(&async_loop_again);
|
||||
cond_var_broadcast(async_tick_start_cond_var);
|
||||
for EachIndex(idx, async_threads_count)
|
||||
{
|
||||
thread_join(async_threads[idx], max_U64);
|
||||
}
|
||||
#endif
|
||||
|
||||
//- rjf: end captures
|
||||
if(capture)
|
||||
{
|
||||
ProfEndCapture();
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params)
|
||||
{
|
||||
TCTX *tctx = tctx_alloc();
|
||||
tctx_select(tctx);
|
||||
entry_point(params);
|
||||
tctx_release(tctx);
|
||||
}
|
||||
|
||||
internal U64
|
||||
update_tick_idx(void)
|
||||
{
|
||||
U64 result = ins_atomic_u64_eval(&global_update_tick_idx);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
update(void)
|
||||
{
|
||||
ProfTick(0);
|
||||
ins_atomic_u64_inc_eval(&global_update_tick_idx);
|
||||
#if defined(FONT_CACHE_H)
|
||||
fnt_frame();
|
||||
#endif
|
||||
#if OS_FEATURE_GRAPHICAL
|
||||
B32 result = frame();
|
||||
#else
|
||||
B32 result = 0;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
async_thread_entry_point(void *params)
|
||||
{
|
||||
LaneCtx lctx = *(LaneCtx *)params;
|
||||
lane_ctx(lctx);
|
||||
is_async_thread = 1;
|
||||
ThreadNameF("async_thread_%I64u", lane_idx());
|
||||
for(;;)
|
||||
{
|
||||
// rjf: wait for signal if we need, otherwise reset loop signal & continue
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
if(!ins_atomic_u32_eval(&async_loop_again))
|
||||
{
|
||||
MutexScope(async_tick_start_mutex) cond_var_wait(async_tick_start_cond_var, async_tick_start_mutex, now_time_us()+1000000);
|
||||
}
|
||||
ins_atomic_u32_eval_assign(&async_loop_again, 0);
|
||||
ins_atomic_u32_eval_assign(&async_loop_again_high_priority, 0);
|
||||
}
|
||||
lane_sync();
|
||||
|
||||
// rjf: do all ticks for all layers
|
||||
ProfScope("async tick")
|
||||
{
|
||||
#if defined(ARTIFACT_CACHE_H)
|
||||
ac_async_tick();
|
||||
#endif
|
||||
#if defined(CONTENT_H)
|
||||
c_async_tick();
|
||||
#endif
|
||||
#if defined(FILE_STREAM_H)
|
||||
fs_async_tick();
|
||||
#endif
|
||||
#if defined(HTTP_H)
|
||||
http_async_tick();
|
||||
#endif
|
||||
#if defined(SYMBOL_SERVER_H)
|
||||
smsv_async_tick();
|
||||
#endif
|
||||
#if defined(DBG_INFO_H)
|
||||
di_async_tick();
|
||||
#endif
|
||||
}
|
||||
|
||||
// rjf: take exit signal; break if set
|
||||
lane_sync();
|
||||
B32 need_exit = 0;
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
need_exit = ins_atomic_u32_eval(&global_async_exit);
|
||||
}
|
||||
lane_sync_u64(&need_exit, 0);
|
||||
if(need_exit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal void
|
||||
main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count)
|
||||
{
|
||||
#if PROFILE_TELEMETRY
|
||||
local_persist U8 tm_data[MB(64)];
|
||||
tmLoadLibrary(TM_RELEASE);
|
||||
tmSetMaxThreadCount(256);
|
||||
tmInitialize(sizeof(tm_data), (char *)tm_data);
|
||||
#endif
|
||||
TCTX tctx;
|
||||
tctx_init_and_equip(&tctx);
|
||||
ThreadNameF("[main thread]");
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, (int)arguments_count, arguments);
|
||||
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
|
||||
B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture"));
|
||||
if(capture)
|
||||
{
|
||||
ProfBeginCapture(arguments[0]);
|
||||
}
|
||||
#if defined(OS_CORE_H) && !defined(OS_INIT_MANUAL)
|
||||
os_init();
|
||||
#endif
|
||||
#if defined(TASK_SYSTEM_H) && !defined(TS_INIT_MANUAL)
|
||||
ts_init();
|
||||
#endif
|
||||
#if defined(HASH_STORE_H) && !defined(HS_INIT_MANUAL)
|
||||
hs_init();
|
||||
#endif
|
||||
#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL)
|
||||
fs_init();
|
||||
#endif
|
||||
#if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL)
|
||||
txt_init();
|
||||
#endif
|
||||
#if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL)
|
||||
dasm_init();
|
||||
#endif
|
||||
#if defined(DI_H) && !defined(DI_INIT_MANUAL)
|
||||
di_init();
|
||||
#endif
|
||||
#if defined(FUZZY_SEARCH_H) && !defined(FZY_INIT_MANUAL)
|
||||
fzy_init();
|
||||
#endif
|
||||
#if defined(TXTI_H) && !defined(TXTI_INIT_MANUAL)
|
||||
txti_init();
|
||||
#endif
|
||||
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
|
||||
dmn_init();
|
||||
#endif
|
||||
#if defined(CTRL_CORE_H) && !defined(CTRL_INIT_MANUAL)
|
||||
ctrl_init();
|
||||
#endif
|
||||
#if defined(OS_GRAPHICAL_H) && !defined(OS_GFX_INIT_MANUAL)
|
||||
os_graphical_init();
|
||||
#endif
|
||||
#if defined(FONT_PROVIDER_H) && !defined(FP_INIT_MANUAL)
|
||||
fp_init();
|
||||
#endif
|
||||
#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL)
|
||||
r_init(&cmdline);
|
||||
#endif
|
||||
#if defined(TEXTURE_CACHE_H) && !defined(TEX_INIT_MANUAL)
|
||||
tex_init();
|
||||
#endif
|
||||
#if defined(GEO_CACHE_H) && !defined(GEO_INIT_MANUAL)
|
||||
geo_init();
|
||||
#endif
|
||||
#if defined(FONT_CACHE_H) && !defined(F_INIT_MANUAL)
|
||||
f_init();
|
||||
#endif
|
||||
#if defined(DF_CORE_H) && !defined(DF_INIT_MANUAL)
|
||||
DF_StateDeltaHistory *hist = df_state_delta_history_alloc();
|
||||
df_core_init(&cmdline, hist);
|
||||
#endif
|
||||
#if defined(DF_GFX_H) && !defined(DF_GFX_INIT_MANUAL)
|
||||
df_gfx_init(update_and_render, df_state_delta_history());
|
||||
#endif
|
||||
entry_point(&cmdline);
|
||||
if(capture)
|
||||
{
|
||||
ProfEndCapture();
|
||||
}
|
||||
scratch_end(scratch);
|
||||
tctx_release();
|
||||
}
|
||||
|
||||
internal void
|
||||
supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params)
|
||||
{
|
||||
TCTX tctx;
|
||||
tctx_init_and_equip(&tctx);
|
||||
entry_point(params);
|
||||
tctx_release();
|
||||
}
|
||||
|
||||
+10
-13
@@ -1,13 +1,10 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_ENTRY_POINT_H
|
||||
#define BASE_ENTRY_POINT_H
|
||||
|
||||
internal void main_thread_base_entry_point(int argc, char **argv);
|
||||
internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params);
|
||||
internal U64 update_tick_idx(void);
|
||||
internal B32 update(void);
|
||||
internal void async_thread_entry_point(void *params);
|
||||
|
||||
#endif // BASE_ENTRY_POINT_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_ENTRY_POINT_H
|
||||
#define BASE_ENTRY_POINT_H
|
||||
|
||||
internal void main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count);
|
||||
internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params);
|
||||
|
||||
#endif // BASE_ENTRY_POINT_H
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Handle Type Functions
|
||||
|
||||
internal File
|
||||
file_zero(void)
|
||||
{
|
||||
File f = {0};
|
||||
return f;
|
||||
}
|
||||
|
||||
internal B32
|
||||
file_match(File a, File b)
|
||||
{
|
||||
B32 result = MemoryMatchStruct(&a, &b);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
|
||||
|
||||
internal String8
|
||||
data_from_file_path(Arena *arena, String8 path)
|
||||
{
|
||||
File file = file_open(AccessFlag_Read|AccessFlag_ShareRead, path);
|
||||
FileProperties props = properties_from_file(file);
|
||||
String8 data = string_from_file_range(arena, file, r1u64(0, props.size));
|
||||
file_close(file);
|
||||
return data;
|
||||
}
|
||||
|
||||
internal B32
|
||||
write_data_to_file_path(String8 path, String8 data)
|
||||
{
|
||||
B32 good = 0;
|
||||
File file = file_open(AccessFlag_Write, path);
|
||||
if(!file_match(file, file_zero()))
|
||||
{
|
||||
U64 bytes_written = file_write(file, r1u64(0, data.size), data.str);
|
||||
good = (bytes_written == data.size);
|
||||
file_close(file);
|
||||
}
|
||||
return good;
|
||||
}
|
||||
|
||||
internal B32
|
||||
write_data_list_to_file_path(String8 path, String8List list)
|
||||
{
|
||||
B32 good = 0;
|
||||
File file = file_open(AccessFlag_Write, path);
|
||||
if(!file_match(file, file_zero()))
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
U64 write_buffer_size = KB(64);
|
||||
U8 *write_buffer = push_array_no_zero(scratch.arena, U8, write_buffer_size);
|
||||
U64 write_buffer_write_pos = 0;
|
||||
U64 write_buffer_read_pos = 0;
|
||||
U64 file_off = 0;
|
||||
{
|
||||
for(String8Node *n = list.first; n != 0; n = n->next)
|
||||
{
|
||||
for(U64 n_off = 0; n_off < n->string.size;)
|
||||
{
|
||||
U64 write_buffer_unconsumed_size = (write_buffer_write_pos - write_buffer_read_pos);
|
||||
U64 write_buffer_available_size = (write_buffer_size - write_buffer_unconsumed_size);
|
||||
if(write_buffer_available_size == 0)
|
||||
{
|
||||
U64 file_write_size = file_write(file, r1u64(file_off, file_off+write_buffer_size), write_buffer);
|
||||
if(file_write_size != write_buffer_size)
|
||||
{
|
||||
goto dbl_break;
|
||||
}
|
||||
file_off += write_buffer_size;
|
||||
write_buffer_read_pos += write_buffer_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
U64 bytes_to_copy = Min(write_buffer_available_size, n->string.size - n_off);
|
||||
write_buffer_write_pos += wrapped_write(write_buffer, write_buffer_size, write_buffer_write_pos, n->string.str + n_off, bytes_to_copy);
|
||||
n_off += bytes_to_copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(write_buffer_write_pos > write_buffer_read_pos)
|
||||
{
|
||||
U64 file_write_size = file_write(file, r1u64(file_off, file_off + (write_buffer_write_pos-write_buffer_read_pos)), write_buffer);
|
||||
file_off += file_write_size;
|
||||
}
|
||||
}
|
||||
dbl_break:;
|
||||
good = (file_off == list.total_size);
|
||||
file_close(file);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
return good;
|
||||
}
|
||||
|
||||
internal B32
|
||||
append_data_to_file_path(String8 path, String8 data)
|
||||
{
|
||||
B32 good = 0;
|
||||
if(data.size != 0)
|
||||
{
|
||||
File file = file_open(AccessFlag_Write|AccessFlag_Append, path);
|
||||
if(!file_match(file, file_zero()))
|
||||
{
|
||||
U64 pos = properties_from_file(file).size;
|
||||
U64 bytes_written = file_write(file, r1u64(pos, pos+data.size), data.str);
|
||||
good = (bytes_written == data.size);
|
||||
file_close(file);
|
||||
}
|
||||
}
|
||||
return good;
|
||||
}
|
||||
|
||||
internal FileID
|
||||
id_from_file_path(String8 path)
|
||||
{
|
||||
File file = file_open(AccessFlag_Read|AccessFlag_ShareRead, path);
|
||||
FileID id = id_from_file(file);
|
||||
file_close(file);
|
||||
return id;
|
||||
}
|
||||
|
||||
internal S64
|
||||
file_id_compare(FileID a, FileID b)
|
||||
{
|
||||
S64 cmp = MemoryCompare((void*)&a.v[0], (void*)&b.v[0], sizeof(a.v));
|
||||
return cmp;
|
||||
}
|
||||
|
||||
internal String8
|
||||
string_from_file_range(Arena *arena, File file, Rng1U64 range)
|
||||
{
|
||||
U64 pre_pos = arena_pos(arena);
|
||||
String8 result;
|
||||
result.size = dim_1u64(range);
|
||||
result.str = push_array_no_zero(arena, U8, result.size);
|
||||
U64 actual_read_size = file_read(file, range, result.str);
|
||||
if(actual_read_size < result.size)
|
||||
{
|
||||
arena_pop_to(arena, pre_pos + actual_read_size);
|
||||
result.size = actual_read_size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
file_read_cstring(Arena *arena, File file, U64 off)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8List block_list = {0};
|
||||
for(U64 cursor = off, stride = 256;; cursor += stride)
|
||||
{
|
||||
U8 *raw_block = push_array_no_zero(scratch.arena, U8, stride);
|
||||
U64 read_size = file_read(file, r1u64(cursor, cursor + stride), raw_block);
|
||||
String8 block = str8_cstring_capped(raw_block, raw_block+read_size);
|
||||
str8_list_push(scratch.arena, &block_list, block);
|
||||
if(read_size != stride || (block.size+1 <= read_size && block.str[block.size] == 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
String8 result = str8_list_join(arena, &block_list, 0);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_FILES_H
|
||||
#define BASE_FILES_H
|
||||
|
||||
typedef U32 FileIterFlags;
|
||||
enum
|
||||
{
|
||||
FileIterFlag_SkipFolders = (1 << 0),
|
||||
FileIterFlag_SkipFiles = (1 << 1),
|
||||
FileIterFlag_SkipHiddenFiles = (1 << 2),
|
||||
FileIterFlag_Done = (1 << 31),
|
||||
};
|
||||
|
||||
typedef struct FileIter FileIter;
|
||||
struct FileIter
|
||||
{
|
||||
FileIterFlags flags;
|
||||
U8 memory[800];
|
||||
};
|
||||
|
||||
typedef struct FileInfo FileInfo;
|
||||
struct FileInfo
|
||||
{
|
||||
String8 name;
|
||||
FileProperties props;
|
||||
};
|
||||
|
||||
// nick: on-disk file identifier
|
||||
typedef struct FileID FileID;
|
||||
struct FileID
|
||||
{
|
||||
U64 v[3];
|
||||
};
|
||||
|
||||
typedef struct File File;
|
||||
struct File
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct FileMap FileMap;
|
||||
struct FileMap
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Handle Type Functions
|
||||
|
||||
internal File file_zero(void);
|
||||
internal B32 file_match(File a, File b);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
|
||||
|
||||
internal String8 data_from_file_path(Arena *arena, String8 path);
|
||||
internal B32 write_data_to_file_path(String8 path, String8 data);
|
||||
internal B32 write_data_list_to_file_path(String8 path, String8List list);
|
||||
internal B32 append_data_to_file_path(String8 path, String8 data);
|
||||
internal FileID id_from_file_path(String8 path);
|
||||
internal S64 file_id_compare(FileID a, FileID b);
|
||||
internal String8 string_from_file_range(Arena *arena, File file, Rng1U64 range);
|
||||
internal String8 file_read_cstring(Arena *arena, File file, U64 off);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl File System (Implemented Per-OS)
|
||||
|
||||
//- rjf: files
|
||||
internal File file_open(AccessFlags flags, String8 path);
|
||||
internal void file_close(File file);
|
||||
internal U64 file_read(File file, Rng1U64 rng, void *out_data);
|
||||
#define file_read_struct(f, off, ptr) file_read((f), r1u64((off), (off)+sizeof(*(ptr))), (ptr))
|
||||
internal U64 file_write(File file, Rng1U64 rng, void *data);
|
||||
internal B32 file_set_times(File file, DateTime time);
|
||||
internal FileProperties properties_from_file(File file);
|
||||
internal FileID id_from_file(File file);
|
||||
internal B32 file_reserve_size(File file, U64 size);
|
||||
internal B32 delete_file_at_path(String8 path);
|
||||
internal B32 copy_file_path(String8 dst, String8 src);
|
||||
internal B32 move_file_path(String8 dst, String8 src);
|
||||
internal String8 full_path_from_path(Arena *arena, String8 path);
|
||||
internal B32 file_path_exists(String8 path);
|
||||
internal B32 folder_path_exists(String8 path);
|
||||
internal FileProperties properties_from_file_path(String8 path);
|
||||
|
||||
//- rjf: file maps
|
||||
internal FileMap file_map_open(AccessFlags flags, File file);
|
||||
internal void file_map_close(FileMap map);
|
||||
internal void * file_map_view_open(FileMap map, AccessFlags flags, Rng1U64 range);
|
||||
internal void file_map_view_close(FileMap map, void *ptr, Rng1U64 range);
|
||||
|
||||
//- rjf: directory iteration
|
||||
internal FileIter *file_iter_begin(Arena *arena, String8 path, FileIterFlags flags);
|
||||
internal B32 file_iter_next(Arena *arena, FileIter *iter, FileInfo *info_out);
|
||||
internal void file_iter_end(FileIter *iter);
|
||||
|
||||
//- rjf: directory creation
|
||||
internal B32 make_directory(String8 path);
|
||||
|
||||
#endif // BASE_FILES_H
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: MD5
|
||||
|
||||
#include "third_party/martins_hash/md5.h"
|
||||
|
||||
internal MD5
|
||||
md5_from_data(String8 data)
|
||||
{
|
||||
md5_ctx ctx = {0};
|
||||
md5_init(&ctx);
|
||||
md5_update(&ctx, (void*)data.str, data.size);
|
||||
MD5 result = {0};
|
||||
md5_finish(&ctx, result.u8);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: SHA
|
||||
|
||||
#include "third_party/martins_hash/sha1.h"
|
||||
#include "third_party/martins_hash/sha256.h"
|
||||
|
||||
internal SHA1
|
||||
sha1_from_data(String8 data)
|
||||
{
|
||||
SHA1 result = {0};
|
||||
{
|
||||
sha1_ctx ctx = {0};
|
||||
sha1_init(&ctx);
|
||||
sha1_update(&ctx, data.str, data.size);
|
||||
sha1_finish(&ctx, result.u8);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal SHA256
|
||||
sha256_from_data(String8 data)
|
||||
{
|
||||
SHA256 result = {0};
|
||||
{
|
||||
sha256_ctx ctx = {0};
|
||||
sha256_init(&ctx);
|
||||
sha256_update(&ctx, data.str, data.size);
|
||||
sha256_finish(&ctx, result.u8);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_HASH_H
|
||||
#define BASE_HASH_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Hash Result Types
|
||||
|
||||
typedef union MD5 MD5;
|
||||
union MD5
|
||||
{
|
||||
U8 u8[16];
|
||||
U16 u16[8];
|
||||
U32 u32[4];
|
||||
U64 u64[2];
|
||||
U128 u128;
|
||||
};
|
||||
|
||||
typedef union SHA1 SHA1;
|
||||
union SHA1
|
||||
{
|
||||
U8 u8[20];
|
||||
};
|
||||
|
||||
typedef union SHA256 SHA256;
|
||||
union SHA256
|
||||
{
|
||||
U8 u8[32];
|
||||
U16 u16[16];
|
||||
U32 u32[8];
|
||||
U64 u64[4];
|
||||
U128 u128[2];
|
||||
U256 u256;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: MD5
|
||||
|
||||
internal MD5 md5_from_data(String8 data);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: SHA
|
||||
|
||||
internal SHA1 sha1_from_data(String8 data);
|
||||
internal SHA256 sha256_from_data(String8 data);
|
||||
|
||||
#endif // BASE_HASH_H
|
||||
+19
-39
@@ -1,39 +1,19 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Base Includes
|
||||
|
||||
#undef LAYER_COLOR
|
||||
#define LAYER_COLOR 0x3399ccff
|
||||
|
||||
#include "base_core.c"
|
||||
#include "base_profile.c"
|
||||
#include "base_memory.c"
|
||||
#include "base_arena.c"
|
||||
#include "base_math.c"
|
||||
#include "base_strings.c"
|
||||
#include "base_memory_map.c"
|
||||
#include "base_hash.c"
|
||||
#include "base_system.c"
|
||||
#include "base_threads.c"
|
||||
#include "base_ring.c"
|
||||
#include "base_thread_context.c"
|
||||
#include "base_files.c"
|
||||
#include "base_shared_memory.c"
|
||||
#include "base_processes.c"
|
||||
#include "base_dynamic_libraries.c"
|
||||
#include "base_command_line.c"
|
||||
#include "base_markup.c"
|
||||
#include "base_meta.c"
|
||||
#include "base_log.c"
|
||||
#include "base_test.c"
|
||||
#include "base_entry_point.c"
|
||||
|
||||
#if OS_WINDOWS
|
||||
# include "win32/base/win32_base.c"
|
||||
#elif OS_LINUX
|
||||
# include "linux/base/linux_base.c"
|
||||
#else
|
||||
# error Operating system backend not found for base layer.
|
||||
#endif
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Base Includes
|
||||
|
||||
#undef RADDBG_LAYER_COLOR
|
||||
#define RADDBG_LAYER_COLOR 0.20f, 0.60f, 0.80f
|
||||
|
||||
#include "base_core.c"
|
||||
#include "base_profile.c"
|
||||
#include "base_arena.c"
|
||||
#include "base_math.c"
|
||||
#include "base_strings.c"
|
||||
#include "base_thread_context.c"
|
||||
#include "base_command_line.c"
|
||||
#include "base_markup.c"
|
||||
#include "base_log.c"
|
||||
#include "base_entry_point.c"
|
||||
|
||||
+23
-43
@@ -1,43 +1,23 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_INC_H
|
||||
#define BASE_INC_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Base Includes
|
||||
|
||||
#include "base_context_cracking.h"
|
||||
|
||||
#include "base_core.h"
|
||||
#include "base_profile.h"
|
||||
#include "base_memory.h"
|
||||
#include "base_arena.h"
|
||||
#include "base_math.h"
|
||||
#include "base_strings.h"
|
||||
#include "base_memory_map.h"
|
||||
#include "base_hash.h"
|
||||
#include "base_system.h"
|
||||
#include "base_threads.h"
|
||||
#include "base_ring.h"
|
||||
#include "base_thread_context.h"
|
||||
#include "base_files.h"
|
||||
#include "base_shared_memory.h"
|
||||
#include "base_processes.h"
|
||||
#include "base_dynamic_libraries.h"
|
||||
#include "base_command_line.h"
|
||||
#include "base_markup.h"
|
||||
#include "base_meta.h"
|
||||
#include "base_log.h"
|
||||
#include "base_test.h"
|
||||
#include "base_entry_point.h"
|
||||
|
||||
#if OS_WINDOWS
|
||||
# include "win32/base/win32_base.h"
|
||||
#elif OS_LINUX
|
||||
# include "linux/base/linux_base.h"
|
||||
#else
|
||||
# error Operating system backend not found for base layer.
|
||||
#endif
|
||||
|
||||
#endif // BASE_INC_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_INC_H
|
||||
#define BASE_INC_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Base Includes
|
||||
|
||||
#include "base_context_cracking.h"
|
||||
|
||||
#include "base_core.h"
|
||||
#include "base_profile.h"
|
||||
#include "base_arena.h"
|
||||
#include "base_math.h"
|
||||
#include "base_strings.h"
|
||||
#include "base_thread_context.h"
|
||||
#include "base_command_line.h"
|
||||
#include "base_markup.h"
|
||||
#include "base_log.h"
|
||||
#include "base_entry_point.h"
|
||||
|
||||
#endif // BASE_INC_H
|
||||
|
||||
+100
-105
@@ -1,105 +1,100 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals/Thread-Locals
|
||||
|
||||
C_LINKAGE thread_static Log *log_active;
|
||||
#if !BUILD_SUPPLEMENTARY_UNIT
|
||||
C_LINKAGE thread_static Log *log_active = 0;
|
||||
#endif
|
||||
|
||||
String8 g_logs_folder;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Creation/Selection
|
||||
|
||||
internal Log *
|
||||
log_alloc(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
Log *log = push_array(arena, Log, 1);
|
||||
log->arena = arena;
|
||||
return log;
|
||||
}
|
||||
|
||||
internal void
|
||||
log_release(Log *log)
|
||||
{
|
||||
arena_release(log->arena);
|
||||
}
|
||||
|
||||
internal void
|
||||
log_select(Log *log)
|
||||
{
|
||||
log_active = log;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Building/Clearing
|
||||
|
||||
internal void
|
||||
log_msg(LogMsgKind kind, String8 string)
|
||||
{
|
||||
if(log_active != 0 && log_active->top_scope != 0)
|
||||
{
|
||||
String8 string_copy = push_str8_copy(log_active->arena, string);
|
||||
str8_list_push(log_active->arena, &log_active->top_scope->strings[kind], string_copy);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
log_msgf(LogMsgKind kind, char *fmt, ...)
|
||||
{
|
||||
if(log_active != 0)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
log_msg(kind, string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Scopes
|
||||
|
||||
internal void
|
||||
log_scope_begin(void)
|
||||
{
|
||||
if(log_active != 0)
|
||||
{
|
||||
U64 pos = arena_pos(log_active->arena);
|
||||
LogScope *scope = push_array(log_active->arena, LogScope, 1);
|
||||
scope->pos = pos;
|
||||
SLLStackPush(log_active->top_scope, scope);
|
||||
}
|
||||
}
|
||||
|
||||
internal LogScopeResult
|
||||
log_scope_end(Arena *arena)
|
||||
{
|
||||
LogScopeResult result = {0};
|
||||
if(log_active != 0)
|
||||
{
|
||||
LogScope *scope = log_active->top_scope;
|
||||
if(scope != 0)
|
||||
{
|
||||
SLLStackPop(log_active->top_scope);
|
||||
if(arena != 0)
|
||||
{
|
||||
for EachEnumVal(LogMsgKind, kind)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8 result_unindented = str8_list_join(scratch.arena, &scope->strings[kind], 0);
|
||||
result.strings[kind] = indented_from_string(arena, result_unindented);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
arena_pop_to(log_active->arena, scope->pos);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals/Thread-Locals
|
||||
|
||||
C_LINKAGE thread_static Log *log_active;
|
||||
#if !BUILD_SUPPLEMENTARY_UNIT
|
||||
C_LINKAGE thread_static Log *log_active = 0;
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Creation/Selection
|
||||
|
||||
internal Log *
|
||||
log_alloc(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
Log *log = push_array(arena, Log, 1);
|
||||
log->arena = arena;
|
||||
return log;
|
||||
}
|
||||
|
||||
internal void
|
||||
log_release(Log *log)
|
||||
{
|
||||
arena_release(log->arena);
|
||||
}
|
||||
|
||||
internal void
|
||||
log_select(Log *log)
|
||||
{
|
||||
log_active = log;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Building/Clearing
|
||||
|
||||
internal void
|
||||
log_msg(LogMsgKind kind, String8 string)
|
||||
{
|
||||
if(log_active != 0 && log_active->top_scope != 0)
|
||||
{
|
||||
String8 string_copy = push_str8_copy(log_active->arena, string);
|
||||
str8_list_push(log_active->arena, &log_active->top_scope->strings[kind], string_copy);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
log_msgf(LogMsgKind kind, char *fmt, ...)
|
||||
{
|
||||
if(log_active != 0)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
log_msg(kind, string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Scopes
|
||||
|
||||
internal void
|
||||
log_scope_begin(void)
|
||||
{
|
||||
if(log_active != 0)
|
||||
{
|
||||
U64 pos = arena_pos(log_active->arena);
|
||||
LogScope *scope = push_array(log_active->arena, LogScope, 1);
|
||||
scope->pos = pos;
|
||||
SLLStackPush(log_active->top_scope, scope);
|
||||
}
|
||||
}
|
||||
|
||||
internal LogScopeResult
|
||||
log_scope_end(Arena *arena)
|
||||
{
|
||||
LogScopeResult result = {0};
|
||||
if(log_active != 0)
|
||||
{
|
||||
LogScope *scope = log_active->top_scope;
|
||||
if(scope != 0)
|
||||
{
|
||||
SLLStackPop(log_active->top_scope);
|
||||
if(arena != 0)
|
||||
{
|
||||
for(EachEnumVal(LogMsgKind, kind))
|
||||
{
|
||||
result.strings[kind] = str8_list_join(arena, &scope->strings[kind], 0);
|
||||
}
|
||||
}
|
||||
arena_pop_to(log_active->arena, scope->pos);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+62
-70
@@ -1,70 +1,62 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_LOG_H
|
||||
#define BASE_LOG_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Types
|
||||
|
||||
typedef enum LogMsgKind
|
||||
{
|
||||
LogMsgKind_Info,
|
||||
LogMsgKind_UserError,
|
||||
LogMsgKind_COUNT
|
||||
}
|
||||
LogMsgKind;
|
||||
|
||||
typedef struct LogScope LogScope;
|
||||
struct LogScope
|
||||
{
|
||||
LogScope *next;
|
||||
U64 pos;
|
||||
String8List strings[LogMsgKind_COUNT];
|
||||
};
|
||||
|
||||
typedef struct LogScopeResult LogScopeResult;
|
||||
struct LogScopeResult
|
||||
{
|
||||
String8 strings[LogMsgKind_COUNT];
|
||||
};
|
||||
|
||||
typedef struct Log Log;
|
||||
struct Log
|
||||
{
|
||||
Arena *arena;
|
||||
LogScope *top_scope;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// Globals
|
||||
|
||||
extern String8 g_logs_folder;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Creation/Selection
|
||||
|
||||
internal Log *log_alloc(void);
|
||||
internal void log_release(Log *log);
|
||||
internal void log_select(Log *log);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Building
|
||||
|
||||
internal void log_msg(LogMsgKind kind, String8 string);
|
||||
internal void log_msgf(LogMsgKind kind, char *fmt, ...);
|
||||
#define log_info(s) log_msg(LogMsgKind_Info, (s))
|
||||
#define log_infof(...) log_msgf(LogMsgKind_Info, __VA_ARGS__)
|
||||
#define log_user_error(s) log_msg(LogMsgKind_UserError, (s))
|
||||
#define log_user_errorf(...) log_msgf(LogMsgKind_UserError, __VA_ARGS__)
|
||||
|
||||
#define LogInfoNamedBlock(s) DeferLoop(log_infof("%S:\n{\n", (s)), log_infof("}\n"))
|
||||
#define LogInfoNamedBlockF(...) DeferLoop((log_infof(__VA_ARGS__), log_infof(":\n{\n")), log_infof("}\n"))
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Scopes
|
||||
|
||||
internal void log_scope_begin(void);
|
||||
internal LogScopeResult log_scope_end(Arena *arena);
|
||||
|
||||
#endif // BASE_LOG_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_LOG_H
|
||||
#define BASE_LOG_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Types
|
||||
|
||||
typedef enum LogMsgKind
|
||||
{
|
||||
LogMsgKind_Info,
|
||||
LogMsgKind_UserError,
|
||||
LogMsgKind_COUNT
|
||||
}
|
||||
LogMsgKind;
|
||||
|
||||
typedef struct LogScope LogScope;
|
||||
struct LogScope
|
||||
{
|
||||
LogScope *next;
|
||||
U64 pos;
|
||||
String8List strings[LogMsgKind_COUNT];
|
||||
};
|
||||
|
||||
typedef struct LogScopeResult LogScopeResult;
|
||||
struct LogScopeResult
|
||||
{
|
||||
String8 strings[LogMsgKind_COUNT];
|
||||
};
|
||||
|
||||
typedef struct Log Log;
|
||||
struct Log
|
||||
{
|
||||
Arena *arena;
|
||||
LogScope *top_scope;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Creation/Selection
|
||||
|
||||
internal Log *log_alloc(void);
|
||||
internal void log_release(Log *log);
|
||||
internal void log_select(Log *log);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Building
|
||||
|
||||
internal void log_msg(LogMsgKind kind, String8 string);
|
||||
internal void log_msgf(LogMsgKind kind, char *fmt, ...);
|
||||
#define log_info(s) log_msg(LogMsgKind_Info, (s))
|
||||
#define log_infof(fmt, ...) log_msgf(LogMsgKind_Info, (fmt), __VA_ARGS__)
|
||||
#define log_user_error(s) log_msg(LogMsgKind_UserError, (s))
|
||||
#define log_user_errorf(fmt, ...) log_msgf(LogMsgKind_UserError, (fmt), __VA_ARGS__)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Scopes
|
||||
|
||||
internal void log_scope_begin(void);
|
||||
internal LogScopeResult log_scope_end(Arena *arena);
|
||||
|
||||
#endif // BASE_LOG_H
|
||||
|
||||
+21
-2
@@ -1,2 +1,21 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal void
|
||||
thread_name(String8 string)
|
||||
{
|
||||
ProfThreadName("%.*s", str8_varg(string));
|
||||
os_set_thread_name(string);
|
||||
}
|
||||
|
||||
internal void
|
||||
thread_namef(char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
thread_name(string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
+12
-20
@@ -1,20 +1,12 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_MARKUP_H
|
||||
#define BASE_MARKUP_H
|
||||
|
||||
#define RADDBG_MARKUP_IMPLEMENTATION
|
||||
#define RADDBG_MARKUP_VSNPRINTF raddbg_vsnprintf
|
||||
#if OS_LINUX
|
||||
# define RADDBG_MARKUP_STUBS
|
||||
#endif
|
||||
#include "lib_raddbg_markup/raddbg_markup.h"
|
||||
|
||||
#if !defined(LAYER_COLOR)
|
||||
# define LAYER_COLOR 0x404040ff
|
||||
#endif
|
||||
|
||||
#define ThreadNameF(...) (set_thread_namef(__VA_ARGS__), raddbg_thread_color_u32(LAYER_COLOR))
|
||||
|
||||
#endif // BASE_MARKUP_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_MARKUP_H
|
||||
#define BASE_MARKUP_H
|
||||
|
||||
internal void thread_name(String8 string);
|
||||
internal void thread_namef(char *fmt, ...);
|
||||
#define ThreadNameF(...) (ProfThreadName(__VA_ARGS__), thread_namef(__VA_ARGS__))
|
||||
#define ThreadName(str) (ProfThreadName("%.*s", str8_varg(str)), thread_name(str))
|
||||
|
||||
#endif // BASE_MARKUP_H
|
||||
|
||||
+616
-849
File diff suppressed because it is too large
Load Diff
+16
-117
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_MATH_H
|
||||
@@ -177,28 +177,6 @@ struct Mat4x4F32
|
||||
|
||||
//- rjf: 1-range
|
||||
|
||||
typedef union Rng1U8 Rng1U8;
|
||||
union Rng1U8
|
||||
{
|
||||
struct
|
||||
{
|
||||
U8 min;
|
||||
U8 max;
|
||||
};
|
||||
U8 v[2];
|
||||
};
|
||||
|
||||
typedef union Rng1U16 Rng1U16;
|
||||
union Rng1U16
|
||||
{
|
||||
struct
|
||||
{
|
||||
U16 min;
|
||||
U16 max;
|
||||
};
|
||||
U16 v[2];
|
||||
};
|
||||
|
||||
typedef union Rng1U32 Rng1U32;
|
||||
union Rng1U32
|
||||
{
|
||||
@@ -349,29 +327,7 @@ union Rng2S64
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Range List Types
|
||||
|
||||
typedef struct Rng1U64Node Rng1U64Node;
|
||||
struct Rng1U64Node
|
||||
{
|
||||
Rng1U64Node *next;
|
||||
Rng1U64 v;
|
||||
};
|
||||
|
||||
typedef struct Rng1U64List Rng1U64List;
|
||||
struct Rng1U64List
|
||||
{
|
||||
Rng1U64Node *first;
|
||||
Rng1U64Node *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct Rng1U64Array Rng1U64Array;
|
||||
struct Rng1U64Array
|
||||
{
|
||||
Rng1U64 *v;
|
||||
U64 count;
|
||||
};
|
||||
//~ rjf: List Types
|
||||
|
||||
typedef struct Rng1S64Node Rng1S64Node;
|
||||
struct Rng1S64Node
|
||||
@@ -396,20 +352,19 @@ struct Rng1S64Array
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scalar Math Ops
|
||||
//~ rjf: Scalar Ops
|
||||
|
||||
#define abs_s64(v) (S64)llabs(v)
|
||||
|
||||
#define sqrt_f32(v) sqrtf(v)
|
||||
#define cbrt_f32(v) cbrtf(v)
|
||||
#define mod_f32(a, b) fmodf((a), (b))
|
||||
#define pow_f32(b, e) powf((b), (e))
|
||||
#define ceil_f32(v) ceilf(v)
|
||||
#define floor_f32(v) floorf(v)
|
||||
#define round_f32(v) roundf(v)
|
||||
#define abs_f32(v) fabsf(v)
|
||||
#define radians_from_turns_f32(v) ((v)*(2*3.1415926535897f))
|
||||
#define turns_from_radians_f32(v) ((v)/(2*3.1415926535897f))
|
||||
#define radians_from_turns_f32(v) ((v)*2*3.1415926535897f)
|
||||
#define turns_from_radians_f32(v) ((v)/2*3.1415926535897f)
|
||||
#define degrees_from_turns_f32(v) ((v)*360.f)
|
||||
#define turns_from_degrees_f32(v) ((v)/360.f)
|
||||
#define degrees_from_radians_f32(v) (degrees_from_turns_f32(turns_from_radians_f32(v)))
|
||||
@@ -419,15 +374,14 @@ struct Rng1S64Array
|
||||
#define tan_f32(v) tanf(radians_from_turns_f32(v))
|
||||
|
||||
#define sqrt_f64(v) sqrt(v)
|
||||
#define cbrt_f64(v) cbrt(v)
|
||||
#define mod_f64(a, b) fmod((a), (b))
|
||||
#define pow_f64(b, e) pow((b), (e))
|
||||
#define ceil_f64(v) ceil(v)
|
||||
#define floor_f64(v) floor(v)
|
||||
#define round_f64(v) round(v)
|
||||
#define abs_f64(v) fabs(v)
|
||||
#define radians_from_turns_f64(v) ((v)*(2*3.1415926535897))
|
||||
#define turns_from_radians_f64(v) ((v)/(2*3.1415926535897))
|
||||
#define radians_from_turns_f64(v) ((v)*2*3.1415926535897)
|
||||
#define turns_from_radians_f64(v) ((v)/2*3.1415926535897)
|
||||
#define degrees_from_turns_f64(v) ((v)*360.0)
|
||||
#define turns_from_degrees_f64(v) ((v)/360.0)
|
||||
#define degrees_from_radians_f64(v) (degrees_from_turns_f64(turns_from_radians_f64(v)))
|
||||
@@ -465,6 +419,7 @@ internal Vec2S64 scale_2s64(Vec2S64 v, S64 s);
|
||||
internal S64 dot_2s64(Vec2S64 a, Vec2S64 b);
|
||||
internal S64 length_squared_2s64(Vec2S64 v);
|
||||
internal S64 length_2s64(Vec2S64 v);
|
||||
internal Vec2S64 normalize_2s64(Vec2S64 v);
|
||||
internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t);
|
||||
|
||||
#define v2s32(x, y) vec_2s32((x), (y))
|
||||
@@ -477,6 +432,7 @@ internal Vec2S32 scale_2s32(Vec2S32 v, S32 s);
|
||||
internal S32 dot_2s32(Vec2S32 a, Vec2S32 b);
|
||||
internal S32 length_squared_2s32(Vec2S32 v);
|
||||
internal S32 length_2s32(Vec2S32 v);
|
||||
internal Vec2S32 normalize_2s32(Vec2S32 v);
|
||||
internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t);
|
||||
|
||||
#define v2s16(x, y) vec_2s16((x), (y))
|
||||
@@ -489,6 +445,7 @@ internal Vec2S16 scale_2s16(Vec2S16 v, S16 s);
|
||||
internal S16 dot_2s16(Vec2S16 a, Vec2S16 b);
|
||||
internal S16 length_squared_2s16(Vec2S16 v);
|
||||
internal S16 length_2s16(Vec2S16 v);
|
||||
internal Vec2S16 normalize_2s16(Vec2S16 v);
|
||||
internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t);
|
||||
|
||||
#define v3f32(x, y, z) vec_3f32((x), (y), (z))
|
||||
@@ -504,7 +461,6 @@ internal F32 length_3f32(Vec3F32 v);
|
||||
internal Vec3F32 normalize_3f32(Vec3F32 v);
|
||||
internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t);
|
||||
internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b);
|
||||
internal Vec3F32 xform_3f32(Vec3F32 v, Mat3x3F32 m);
|
||||
|
||||
#define v3s32(x, y, z) vec_3s32((x), (y), (z))
|
||||
internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z);
|
||||
@@ -516,6 +472,7 @@ internal Vec3S32 scale_3s32(Vec3S32 v, S32 s);
|
||||
internal S32 dot_3s32(Vec3S32 a, Vec3S32 b);
|
||||
internal S32 length_squared_3s32(Vec3S32 v);
|
||||
internal S32 length_3s32(Vec3S32 v);
|
||||
internal Vec3S32 normalize_3s32(Vec3S32 v);
|
||||
internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t);
|
||||
internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b);
|
||||
|
||||
@@ -542,6 +499,7 @@ internal Vec4S32 scale_4s32(Vec4S32 v, S32 s);
|
||||
internal S32 dot_4s32(Vec4S32 a, Vec4S32 b);
|
||||
internal S32 length_squared_4s32(Vec4S32 v);
|
||||
internal S32 length_4s32(Vec4S32 v);
|
||||
internal Vec4S32 normalize_4s32(Vec4S32 v);
|
||||
internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t);
|
||||
|
||||
////////////////////////////////
|
||||
@@ -563,37 +521,11 @@ internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b);
|
||||
internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale);
|
||||
internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m);
|
||||
internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat);
|
||||
internal Mat4x4F32 transpose_4x4f32(Mat4x4F32 mat);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Range Ops
|
||||
|
||||
#define r1u8(min, max) rng_1u8((min), (max))
|
||||
#define r1u8s(min, size) rng_1u8((min), (min)+(size))
|
||||
internal Rng1U8 rng_1u8(U8 min, U8 max);
|
||||
internal Rng1U8 shift_1u8(Rng1U8 r, U8 x);
|
||||
internal Rng1U8 pad_1u8(Rng1U8 r, U8 x);
|
||||
internal U8 center_1u8(Rng1U8 r);
|
||||
internal B32 contains_1u8(Rng1U8 r, U8 x);
|
||||
internal U8 dim_1u8(Rng1U8 r);
|
||||
internal Rng1U8 union_1u8(Rng1U8 a, Rng1U8 b);
|
||||
internal Rng1U8 intersect_1u8(Rng1U8 a, Rng1U8 b);
|
||||
internal U8 clamp_1u8(Rng1U8 r, U8 v);
|
||||
|
||||
#define r1u16(min, max) rng_1u16((min), (max))
|
||||
#define r1u16s(min, size) rng_1u16((min), (min)+(size))
|
||||
internal Rng1U16 rng_1u16(U16 min, U16 max);
|
||||
internal Rng1U16 shift_1u16(Rng1U16 r, U16 x);
|
||||
internal Rng1U16 pad_1u16(Rng1U16 r, U16 x);
|
||||
internal U16 center_1u16(Rng1U16 r);
|
||||
internal B32 contains_1u16(Rng1U16 r, U16 x);
|
||||
internal U16 dim_1u16(Rng1U16 r);
|
||||
internal Rng1U16 union_1u16(Rng1U16 a, Rng1U16 b);
|
||||
internal Rng1U16 intersect_1u16(Rng1U16 a, Rng1U16 b);
|
||||
internal U16 clamp_1u16(Rng1U16 r, U16 v);
|
||||
|
||||
#define r1u32(min, max) rng_1u32((min), (max))
|
||||
#define r1u32s(min, size) rng_1u32((min), (min)+(size))
|
||||
internal Rng1U32 rng_1u32(U32 min, U32 max);
|
||||
internal Rng1U32 shift_1u32(Rng1U32 r, U32 x);
|
||||
internal Rng1U32 pad_1u32(Rng1U32 r, U32 x);
|
||||
@@ -605,7 +537,6 @@ internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b);
|
||||
internal U32 clamp_1u32(Rng1U32 r, U32 v);
|
||||
|
||||
#define r1s32(min, max) rng_1s32((min), (max))
|
||||
#define r1s32s(min,size) rng_1s32((min), (min)+(size))
|
||||
internal Rng1S32 rng_1s32(S32 min, S32 max);
|
||||
internal Rng1S32 shift_1s32(Rng1S32 r, S32 x);
|
||||
internal Rng1S32 pad_1s32(Rng1S32 r, S32 x);
|
||||
@@ -617,7 +548,6 @@ internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b);
|
||||
internal S32 clamp_1s32(Rng1S32 r, S32 v);
|
||||
|
||||
#define r1u64(min, max) rng_1u64((min), (max))
|
||||
#define r1u64s(min, size) rng_1u64((min), (min)+(size))
|
||||
internal Rng1U64 rng_1u64(U64 min, U64 max);
|
||||
internal Rng1U64 shift_1u64(Rng1U64 r, U64 x);
|
||||
internal Rng1U64 pad_1u64(Rng1U64 r, U64 x);
|
||||
@@ -629,7 +559,6 @@ internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b);
|
||||
internal U64 clamp_1u64(Rng1U64 r, U64 v);
|
||||
|
||||
#define r1s64(min, max) rng_1s64((min), (max))
|
||||
#define r1s64s(min, size) rng_1s64((min), (min)+(size))
|
||||
internal Rng1S64 rng_1s64(S64 min, S64 max);
|
||||
internal Rng1S64 shift_1s64(Rng1S64 r, S64 x);
|
||||
internal Rng1S64 pad_1s64(Rng1S64 r, S64 x);
|
||||
@@ -641,7 +570,6 @@ internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b);
|
||||
internal S64 clamp_1s64(Rng1S64 r, S64 v);
|
||||
|
||||
#define r1f32(min, max) rng_1f32((min), (max))
|
||||
#define r1f32s(min, size) rng_1f32((min), (min)+(size))
|
||||
internal Rng1F32 rng_1f32(F32 min, F32 max);
|
||||
internal Rng1F32 shift_1f32(Rng1F32 r, F32 x);
|
||||
internal Rng1F32 pad_1f32(Rng1F32 r, F32 x);
|
||||
@@ -653,7 +581,6 @@ internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b);
|
||||
internal F32 clamp_1f32(Rng1F32 r, F32 v);
|
||||
|
||||
#define r2s16(min, max) rng_2s16((min), (max))
|
||||
#define r2s16s(min, size) r2s16((min), (min)+(size))
|
||||
#define r2s16p(x, y, z, w) r2s16(v2s16((x), (y)), v2s16((z), (w)))
|
||||
internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max);
|
||||
internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x);
|
||||
@@ -666,7 +593,6 @@ internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b);
|
||||
internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v);
|
||||
|
||||
#define r2s32(min, max) rng_2s32((min), (max))
|
||||
#define r2s32s(min, size) r2s32((min), (min)+(size))
|
||||
#define r2s32p(x, y, z, w) r2s32(v2s32((x), (y)), v2s32((z), (w)))
|
||||
internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max);
|
||||
internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x);
|
||||
@@ -679,7 +605,6 @@ internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b);
|
||||
internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v);
|
||||
|
||||
#define r2s64(min, max) rng_2s64((min), (max))
|
||||
#define r2s64s(min, size) r2s64((min), (min)+(size))
|
||||
#define r2s64p(x, y, z, w) r2s64(v2s64((x), (y)), v2s64((z), (w)))
|
||||
internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max);
|
||||
internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x);
|
||||
@@ -692,7 +617,6 @@ internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b);
|
||||
internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v);
|
||||
|
||||
#define r2f32(min, max) rng_2f32((min), (max))
|
||||
#define r2f32s(min, size) r2f32((min), (min)+(size))
|
||||
#define r2f32p(x, y, z, w) r2f32(v2f32((x), (y)), v2f32((z), (w)))
|
||||
internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max);
|
||||
internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x);
|
||||
@@ -705,46 +629,21 @@ internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b);
|
||||
internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Color Operations
|
||||
//~ rjf: Miscellaneous Ops
|
||||
|
||||
//- rjf: hsv <-> rgb
|
||||
internal Vec3F32 hsv_from_rgb(Vec3F32 rgb);
|
||||
internal Vec3F32 rgb_from_hsv(Vec3F32 hsv);
|
||||
internal Vec4F32 hsva_from_rgba(Vec4F32 rgba);
|
||||
internal Vec4F32 rgba_from_hsva(Vec4F32 hsva);
|
||||
|
||||
//- rjf: srgb <-> linear
|
||||
internal Vec3F32 linear_from_srgb(Vec3F32 srgb);
|
||||
internal Vec3F32 srgb_from_linear(Vec3F32 linear);
|
||||
internal Vec4F32 linear_from_srgba(Vec4F32 srgba);
|
||||
internal Vec4F32 srgba_from_linear(Vec4F32 linear);
|
||||
|
||||
//- rjf: oklab <-> linear
|
||||
internal Vec3F32 oklab_from_linear(Vec3F32 linear);
|
||||
internal Vec3F32 linear_from_oklab(Vec3F32 oklab);
|
||||
internal Vec4F32 oklab_from_lineara(Vec4F32 lineara);
|
||||
internal Vec4F32 lineara_from_oklab(Vec4F32 oklab);
|
||||
|
||||
//- rjf: rgba <-> u32
|
||||
internal U32 u32_from_rgba(Vec4F32 rgba);
|
||||
internal Vec4F32 rgba_from_u32(U32 hex);
|
||||
internal U32 u32_from_rgba(Vec4F32 rgba);
|
||||
|
||||
#define rgba_from_u32_lit_comp(h) { (((h)&0xff000000)>>24)/255.f, (((h)&0x00ff0000)>>16)/255.f, (((h)&0x0000ff00)>> 8)/255.f, (((h)&0x000000ff)>> 0)/255.f }
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: List Type Functions
|
||||
|
||||
internal void rng1u64_list_push_node(Rng1U64List *list, Rng1U64Node *n);
|
||||
internal Rng1U64Node * rng1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 rng);
|
||||
internal void rng1u64_list_concat(Rng1U64List *list, Rng1U64List *to_concat);
|
||||
internal Rng1U64Array rng1u64_array_from_list(Arena *arena, Rng1U64List *list);
|
||||
internal U64 rng1u64_array_num_from_value__binary_search(Rng1U64Array *array, U64 value);
|
||||
|
||||
internal void rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng);
|
||||
internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: N -> M Element Subdivision
|
||||
|
||||
internal Rng1U64 m_range_from_n_idx_m_count(U64 n_idx, U64 n_count, U64 m_count);
|
||||
|
||||
#endif //BASE_MATH_H
|
||||
#endif // BASE_MATH_H
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_MEMORY_H
|
||||
#define BASE_MEMORY_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Platform Memory Allocation
|
||||
|
||||
//- rjf: basic
|
||||
internal void *reserve_memory(U64 size);
|
||||
internal B32 commit_memory(void *ptr, U64 size);
|
||||
internal void decommit_memory(void *ptr, U64 size);
|
||||
internal void release_memory(void *ptr, U64 size);
|
||||
|
||||
//- rjf: large pages
|
||||
internal void *reserve_memory_large(U64 size);
|
||||
internal B32 commit_memory_large(void *ptr, U64 size);
|
||||
|
||||
#endif // BASE_MEMORY_H
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Memory Map Functions
|
||||
|
||||
internal void
|
||||
memory_map_push(Arena *arena, MemoryMap *map, Rng1U64 vaddr_range, void *data)
|
||||
{
|
||||
MemoryMapRangeNode *n = push_array(arena, MemoryMapRangeNode, 1);
|
||||
n->v.vaddr_range = vaddr_range;
|
||||
n->v.base = data;
|
||||
SLLQueuePush(map->first_range, map->last_range, n);
|
||||
}
|
||||
|
||||
internal U64
|
||||
memory_map_read(MemoryMap *map, Rng1U64 range, void *dst)
|
||||
{
|
||||
U64 dst_vaddr = range.min;
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
U64 start_dst_vaddr = dst_vaddr;
|
||||
B32 found = 0;
|
||||
for(MemoryMapRangeNode *n = map->first_range; n != 0; n = n->next)
|
||||
{
|
||||
if(contains_1u64(n->v.vaddr_range, dst_vaddr))
|
||||
{
|
||||
U64 src_off = dst_vaddr - n->v.vaddr_range.min;
|
||||
U64 num_bytes_possible = n->v.vaddr_range.max - dst_vaddr;
|
||||
U64 num_bytes_needed = range.max - dst_vaddr;
|
||||
U64 num_bytes_to_read = Min(num_bytes_needed, num_bytes_possible);
|
||||
MemoryCopy((U8 *)dst + (dst_vaddr - range.min), (U8 *)n->v.base + src_off, num_bytes_to_read);
|
||||
dst_vaddr += num_bytes_to_read;
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if(!found || start_dst_vaddr == dst_vaddr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
U64 bytes_read = (dst_vaddr - range.min);
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
internal String8
|
||||
memory_map_data_from_range(Arena *arena, MemoryMap *map, Rng1U64 range)
|
||||
{
|
||||
String8 result = {0};
|
||||
result.size = dim_1u64(range);
|
||||
result.str = push_array(arena, U8, result.size);
|
||||
memory_map_read(map, range, result.str);
|
||||
return result;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_MEMORY_MAP_H
|
||||
#define BASE_MEMORY_MAP_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Memory Map Types
|
||||
|
||||
typedef struct MemoryMapRange MemoryMapRange;
|
||||
struct MemoryMapRange
|
||||
{
|
||||
Rng1U64 vaddr_range;
|
||||
void *base;
|
||||
};
|
||||
|
||||
typedef struct MemoryMapRangeNode MemoryMapRangeNode;
|
||||
struct MemoryMapRangeNode
|
||||
{
|
||||
MemoryMapRangeNode *next;
|
||||
MemoryMapRange v;
|
||||
};
|
||||
|
||||
typedef struct MemoryMap MemoryMap;
|
||||
struct MemoryMap
|
||||
{
|
||||
MemoryMapRangeNode *first_range;
|
||||
MemoryMapRangeNode *last_range;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Memory Map Functions
|
||||
|
||||
internal void memory_map_push(Arena *arena, MemoryMap *map, Rng1U64 vaddr_range, void *data);
|
||||
internal U64 memory_map_read(MemoryMap *map, Rng1U64 range, void *dst);
|
||||
#define memory_map_read_struct(map, vaddr, ptr) memory_map_read((map), r1u64((vaddr), (vaddr)+sizeof(*(ptr))), (ptr))
|
||||
internal String8 memory_map_data_from_range(Arena *arena, MemoryMap *map, Rng1U64 range);
|
||||
|
||||
#endif // BASE_MEMORY_MAP_H
|
||||
@@ -1,421 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Type Info Lookups
|
||||
|
||||
internal Member *
|
||||
member_from_name(Type *type, String8 name)
|
||||
{
|
||||
Member *member = &member_nil;
|
||||
if(type->members != 0 && name.size != 0)
|
||||
{
|
||||
for(U64 idx = 0; idx < type->count; idx += 1)
|
||||
{
|
||||
if(str8_match(type->members[idx].name, name, 0))
|
||||
{
|
||||
member = &type->members[idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return member;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Type Info * Instance Operations
|
||||
|
||||
internal void
|
||||
typed_data_rebase_ptrs(Type *type, String8 data, void *base_ptr)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
typedef struct RebaseTypeTask RebaseTypeTask;
|
||||
struct RebaseTypeTask
|
||||
{
|
||||
RebaseTypeTask *next;
|
||||
Type *type;
|
||||
U8 *ptr;
|
||||
};
|
||||
RebaseTypeTask start_task = {0, type, data.str};
|
||||
RebaseTypeTask *first_task = &start_task;
|
||||
RebaseTypeTask *last_task = first_task;
|
||||
for(RebaseTypeTask *t = first_task; t != 0; t = t->next)
|
||||
{
|
||||
switch(t->type->kind)
|
||||
{
|
||||
default:{}break;
|
||||
case TypeKind_Ptr:
|
||||
{
|
||||
*(U64 *)t->ptr = ((U64)(*(U8 **)t->ptr - (U8 *)base_ptr));
|
||||
}break;
|
||||
case TypeKind_Array:
|
||||
{
|
||||
for(U64 idx = 0; idx < t->type->count; idx += 1)
|
||||
{
|
||||
RebaseTypeTask *task = push_array(scratch.arena, RebaseTypeTask, 1);
|
||||
task->type = t->type->direct;
|
||||
task->ptr = t->ptr + t->type->direct->size * idx;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}
|
||||
}break;
|
||||
case TypeKind_Struct:
|
||||
{
|
||||
for(U64 idx = 0; idx < t->type->count; idx += 1)
|
||||
{
|
||||
Member *member = &t->type->members[idx];
|
||||
RebaseTypeTask *task = push_array(scratch.arena, RebaseTypeTask, 1);
|
||||
task->type = member->type;
|
||||
task->ptr = t->ptr + member->value;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}
|
||||
}break;
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal String8
|
||||
serialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8List strings = {0};
|
||||
str8_serial_begin(scratch.arena, &strings);
|
||||
{
|
||||
typedef struct SerializeTypeTask SerializeTypeTask;
|
||||
struct SerializeTypeTask
|
||||
{
|
||||
SerializeTypeTask *next;
|
||||
Type *type;
|
||||
U64 count;
|
||||
U8 *src;
|
||||
Type *containing_type;
|
||||
U8 *containing_ptr;
|
||||
B32 is_post_header;
|
||||
};
|
||||
SerializeTypeTask start_task = {0, type, 1, data.str};
|
||||
SerializeTypeTask *first_task = &start_task;
|
||||
SerializeTypeTask *last_task = first_task;
|
||||
for(SerializeTypeTask *t = first_task; t != 0; t = t->next)
|
||||
{
|
||||
switch(t->type->kind)
|
||||
{
|
||||
//- rjf: leaf -> just copy the data directly
|
||||
default:
|
||||
if(TypeKind_FirstLeaf <= t->type->kind && t->type->kind <= TypeKind_LastLeaf)
|
||||
{
|
||||
str8_serial_push_string(scratch.arena, &strings, str8(t->src, t->type->size*t->count));
|
||||
}break;
|
||||
|
||||
//- rjf: pointers -> try to interpret/understand pointer & read/write, otherwise just write as plain data
|
||||
case TypeKind_Ptr:
|
||||
{
|
||||
// rjf: unpack info about this pointer
|
||||
TypeSerializePtrRefInfo *ptr_ref_info = 0;
|
||||
for(U64 idx = 0; idx < params->ptr_ref_infos_count; idx += 1)
|
||||
{
|
||||
if(params->ptr_ref_infos[idx].type == t->type->direct)
|
||||
{
|
||||
ptr_ref_info = ¶ms->ptr_ref_infos[idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: indexification -> subtract base, divide direct size, write index
|
||||
if(ptr_ref_info != 0 && ptr_ref_info->indexify_base != 0)
|
||||
{
|
||||
U64 ptr_value = 0;
|
||||
MemoryCopy(&ptr_value, t->src, sizeof(ptr_value));
|
||||
U64 ptr_write_value = ((U64)((U8 *)ptr_value - (U8 *)ptr_ref_info->indexify_base)/t->type->direct->size);
|
||||
str8_serial_push_struct(scratch.arena, &strings, &ptr_write_value);
|
||||
}
|
||||
|
||||
// rjf: offsetification -> subtract base, write offsets
|
||||
else if(ptr_ref_info != 0 && ptr_ref_info->offsetify_base != 0)
|
||||
{
|
||||
U64 ptr_value = 0;
|
||||
MemoryCopy(&ptr_value, t->src, sizeof(ptr_value));
|
||||
U64 ptr_write_value = (U64)((U8 *)ptr_value - (U8 *)ptr_ref_info->offsetify_base);
|
||||
str8_serial_push_struct(scratch.arena, &strings, &ptr_write_value);
|
||||
}
|
||||
|
||||
// rjf: size-by-member (pre-header): still potentially dependent on other members which
|
||||
// delimit our size, so push a new post-header task for pointer.
|
||||
else if(t->type->count_delimiter_name.size != 0 && !t->is_post_header)
|
||||
{
|
||||
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
|
||||
task->type = t->type;
|
||||
task->count = t->count;
|
||||
task->src = t->src;
|
||||
task->containing_type = t->containing_type;
|
||||
task->containing_ptr = t->containing_ptr;
|
||||
task->is_post_header = 1;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}
|
||||
|
||||
// rjf: size-by-member (post-header): all flat parts of containing struct have been
|
||||
// iterated, so now we can read the size, & descend to new task to read pointer
|
||||
// destination contents
|
||||
else if(t->type->count_delimiter_name.size != 0 && t->is_post_header)
|
||||
{
|
||||
// rjf: determine count of this pointer
|
||||
U64 count = 0;
|
||||
{
|
||||
Member *count_member = member_from_name(t->containing_type, t->type->count_delimiter_name);
|
||||
MemoryCopy(&count, t->containing_ptr + count_member->value, count_member->type->size);
|
||||
}
|
||||
|
||||
// rjf: push task
|
||||
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
|
||||
task->type = t->type->direct;
|
||||
task->count = count;
|
||||
task->src = *(void **)t->src;
|
||||
task->containing_type = t->containing_type;
|
||||
task->containing_ptr = t->containing_ptr;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}
|
||||
|
||||
// rjf: catch-all: write pointer value
|
||||
else
|
||||
{
|
||||
str8_serial_push_string(scratch.arena, &strings, str8(t->src, t->type->size*t->count));
|
||||
}
|
||||
}break;
|
||||
|
||||
//- rjf: arrays -> descend to underlying type, + count
|
||||
case TypeKind_Array:
|
||||
{
|
||||
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
|
||||
task->type = t->type->direct;
|
||||
task->count = t->type->count;
|
||||
task->src = t->src;
|
||||
task->containing_type = t->containing_type;
|
||||
task->containing_ptr = t->containing_ptr;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}break;
|
||||
|
||||
//- rjf: struct -> descend to members
|
||||
case TypeKind_Struct:
|
||||
{
|
||||
U64 off = 0;
|
||||
for(U64 idx = 0; idx < t->count; idx += 1)
|
||||
{
|
||||
for(U64 member_idx = 0; member_idx < t->type->count; member_idx += 1)
|
||||
{
|
||||
if(t->type->members[member_idx].flags & MemberFlag_DoNotSerialize)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
|
||||
task->type = t->type->members[member_idx].type;
|
||||
task->count = 1;
|
||||
task->src = t->src + idx*t->type->size + t->type->members[member_idx].value;
|
||||
task->containing_type = t->type;
|
||||
task->containing_ptr = t->src;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}
|
||||
}
|
||||
}break;
|
||||
|
||||
//- rjf: enum -> descend to basic type interpretation
|
||||
case TypeKind_Enum:
|
||||
{
|
||||
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
|
||||
task->type = t->type->direct;
|
||||
task->count = t->count;
|
||||
task->src = t->src;
|
||||
task->containing_type = t->containing_type;
|
||||
task->containing_ptr = t->containing_ptr;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}break;
|
||||
}
|
||||
}
|
||||
}
|
||||
String8 result = str8_serial_end(arena, &strings);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
deserialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
|
||||
{
|
||||
String8 result = {0};
|
||||
result.size = type->size;
|
||||
result.str = push_array(arena, U8, result.size);
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
typedef struct DeserializeTypeTask DeserializeTypeTask;
|
||||
struct DeserializeTypeTask
|
||||
{
|
||||
DeserializeTypeTask *next;
|
||||
Type *type;
|
||||
U64 count;
|
||||
U8 *dst;
|
||||
Type *containing_type;
|
||||
U8 *containing_ptr;
|
||||
B32 is_post_header;
|
||||
};
|
||||
U64 read_off = 0;
|
||||
DeserializeTypeTask start_task = {0, type, 1, result.str};
|
||||
DeserializeTypeTask *first_task = &start_task;
|
||||
DeserializeTypeTask *last_task = first_task;
|
||||
for(DeserializeTypeTask *t = first_task; t != 0; t = t->next)
|
||||
{
|
||||
U8 *t_src = data.str + read_off;
|
||||
switch(t->type->kind)
|
||||
{
|
||||
//- rjf: leaf -> copy the data directly
|
||||
default:
|
||||
if(TypeKind_FirstLeaf <= t->type->kind && t->type->kind <= TypeKind_LastLeaf)
|
||||
{
|
||||
MemoryCopy(t->dst, t_src, t->type->size*t->count);
|
||||
read_off += t->type->size*t->count;
|
||||
}break;
|
||||
|
||||
//- rjf: pointers -> try to interpret/understand pointer & read/write, otherwise skip
|
||||
case TypeKind_Ptr:
|
||||
{
|
||||
// rjf: unpack info about this pointer
|
||||
TypeSerializePtrRefInfo *ptr_ref_info = 0;
|
||||
for(U64 idx = 0; idx < params->ptr_ref_infos_count; idx += 1)
|
||||
{
|
||||
if(params->ptr_ref_infos[idx].type == t->type->direct)
|
||||
{
|
||||
ptr_ref_info = ¶ms->ptr_ref_infos[idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: indexification -> add base, multiply direct size
|
||||
if(ptr_ref_info != 0 && ptr_ref_info->indexify_base != 0)
|
||||
{
|
||||
U64 ptr_value = 0;
|
||||
MemoryCopy(&ptr_value, t_src, sizeof(ptr_value));
|
||||
U64 ptr_write_value = (ptr_value + (U64)ptr_ref_info->indexify_base) * t->type->direct->size;
|
||||
MemoryCopy(t->dst, &ptr_write_value, sizeof(ptr_write_value));
|
||||
read_off += sizeof(ptr_value);
|
||||
}
|
||||
|
||||
// rjf: offsetification -> subtract base, write offsets
|
||||
else if(ptr_ref_info != 0 && ptr_ref_info->offsetify_base != 0)
|
||||
{
|
||||
U64 ptr_value = 0;
|
||||
MemoryCopy(&ptr_value, t_src, sizeof(ptr_value));
|
||||
U64 ptr_write_value = ptr_value + (U64)ptr_ref_info->offsetify_base;
|
||||
MemoryCopy(t->dst, &ptr_write_value, sizeof(ptr_write_value));
|
||||
read_off += sizeof(ptr_value);
|
||||
}
|
||||
|
||||
// rjf: size-by-member (pre-header): still potentially dependent on other members which
|
||||
// delimit our size, so push a new post-header task for pointer.
|
||||
else if(t->type->count_delimiter_name.size != 0 && !t->is_post_header)
|
||||
{
|
||||
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
|
||||
task->type = t->type;
|
||||
task->count = t->count;
|
||||
task->dst = t->dst;
|
||||
task->containing_type = t->containing_type;
|
||||
task->containing_ptr = t->containing_ptr;
|
||||
task->is_post_header = 1;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}
|
||||
|
||||
// rjf: size-by-member (post-header): all flat parts of containing struct have been
|
||||
// iterated, so now we can read the size, & descend to new task to read pointer
|
||||
// destination contents
|
||||
else if(t->type->count_delimiter_name.size != 0 && t->is_post_header)
|
||||
{
|
||||
// rjf: determine count of this pointer
|
||||
U64 count = 0;
|
||||
{
|
||||
Member *count_member = member_from_name(t->containing_type, t->type->count_delimiter_name);
|
||||
MemoryCopy(&count, t->containing_ptr + count_member->value, count_member->type->size);
|
||||
}
|
||||
|
||||
// rjf: allocate buffer for pointer destination; write address into pointer value slot
|
||||
U64 ptr_dest_buffer_size = (count+1)*t->type->direct->size;
|
||||
U8 *ptr_dest_buffer = push_array(arena, U8, ptr_dest_buffer_size);
|
||||
MemoryCopy(t->dst, &ptr_dest_buffer, sizeof(ptr_dest_buffer));
|
||||
|
||||
// rjf: push task
|
||||
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
|
||||
task->type = t->type->direct;
|
||||
task->count = count;
|
||||
task->dst = ptr_dest_buffer;
|
||||
task->containing_type = t->containing_type;
|
||||
task->containing_ptr = t->containing_ptr;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}
|
||||
|
||||
// rjf: catch-all: read pointer value
|
||||
else
|
||||
{
|
||||
MemoryCopy(t->dst, t_src, t->type->size*t->count);
|
||||
read_off += t->type->size*t->count;
|
||||
}
|
||||
}break;
|
||||
|
||||
//- rjf: arrays -> descend to underlying type, + count
|
||||
case TypeKind_Array:
|
||||
{
|
||||
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
|
||||
task->type = t->type->direct;
|
||||
task->count = t->type->count;
|
||||
task->dst = t->dst;
|
||||
task->containing_type = t->containing_type;
|
||||
task->containing_ptr = t->containing_ptr;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}break;
|
||||
|
||||
//- rjf: struct -> descend to members
|
||||
case TypeKind_Struct:
|
||||
{
|
||||
for(U64 idx = 0; idx < t->count; idx += 1)
|
||||
{
|
||||
for(U64 member_idx = 0; member_idx < t->type->count; member_idx += 1)
|
||||
{
|
||||
if(t->type->members[member_idx].flags & MemberFlag_DoNotSerialize)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
|
||||
task->type = t->type->members[member_idx].type;
|
||||
task->count = 1;
|
||||
task->dst = t->dst + idx*t->type->size + t->type->members[member_idx].value;
|
||||
task->containing_type = t->type;
|
||||
task->containing_ptr = t->dst;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}
|
||||
}
|
||||
}break;
|
||||
|
||||
//- rjf: enum -> descend to basic type interpretation
|
||||
case TypeKind_Enum:
|
||||
{
|
||||
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
|
||||
task->type = t->type->direct;
|
||||
task->count = t->count;
|
||||
task->dst = t->dst;
|
||||
task->containing_type = t->containing_type;
|
||||
task->containing_ptr = t->containing_ptr;
|
||||
SLLQueuePush(first_task, last_task, task);
|
||||
}break;
|
||||
}
|
||||
}
|
||||
if(params->advance_out != 0)
|
||||
{
|
||||
params->advance_out[0] = read_off;
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
deep_copy_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8 data_srlz = serialized_from_typed_data(scratch.arena, type, data, params);
|
||||
String8 data_copy = deserialized_from_typed_data(arena, type, data_srlz, params);
|
||||
scratch_end(scratch);
|
||||
return data_copy;
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_META_H
|
||||
#define BASE_META_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Meta Markup Features
|
||||
|
||||
#define EmbedFile(name, path)
|
||||
#define TweakB32(name, default) (TWEAK_##name)
|
||||
#define TweakF32(name, default, min, max) (TWEAK_##name)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Tweak Info Tables
|
||||
|
||||
typedef struct TweakB32Info TweakB32Info;
|
||||
struct TweakB32Info
|
||||
{
|
||||
String8 name;
|
||||
B32 default_value;
|
||||
B32 *value_ptr;
|
||||
};
|
||||
|
||||
typedef struct TweakF32Info TweakF32Info;
|
||||
struct TweakF32Info
|
||||
{
|
||||
String8 name;
|
||||
F32 default_value;
|
||||
Rng1F32 value_range;
|
||||
F32 *value_ptr;
|
||||
};
|
||||
|
||||
typedef struct TweakB32InfoTable TweakB32InfoTable;
|
||||
struct TweakB32InfoTable
|
||||
{
|
||||
TweakB32Info *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct TweakF32InfoTable TweakF32InfoTable;
|
||||
struct TweakF32InfoTable
|
||||
{
|
||||
TweakF32Info *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct EmbedInfo EmbedInfo;
|
||||
struct EmbedInfo
|
||||
{
|
||||
String8 name;
|
||||
String8 *data;
|
||||
U128 *hash;
|
||||
};
|
||||
|
||||
typedef struct EmbedInfoTable EmbedInfoTable;
|
||||
struct EmbedInfoTable
|
||||
{
|
||||
EmbedInfo *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Type Info Types
|
||||
|
||||
typedef enum TypeKind
|
||||
{
|
||||
TypeKind_Null,
|
||||
|
||||
// rjf: leaves
|
||||
TypeKind_Void, TypeKind_FirstLeaf = TypeKind_Void,
|
||||
TypeKind_U8,
|
||||
TypeKind_U16,
|
||||
TypeKind_U32,
|
||||
TypeKind_U64,
|
||||
TypeKind_S8,
|
||||
TypeKind_S16,
|
||||
TypeKind_S32,
|
||||
TypeKind_S64,
|
||||
TypeKind_B8,
|
||||
TypeKind_B16,
|
||||
TypeKind_B32,
|
||||
TypeKind_B64,
|
||||
TypeKind_F32,
|
||||
TypeKind_F64, TypeKind_LastLeaf = TypeKind_F64,
|
||||
|
||||
// rjf: operators
|
||||
TypeKind_Ptr,
|
||||
TypeKind_Array,
|
||||
|
||||
// rjf: user-defined-types
|
||||
TypeKind_Struct,
|
||||
TypeKind_Union,
|
||||
TypeKind_Enum,
|
||||
|
||||
TypeKind_COUNT
|
||||
}
|
||||
TypeKind;
|
||||
|
||||
typedef U32 TypeFlags;
|
||||
enum
|
||||
{
|
||||
TypeFlag_IsPlainText = (1<<0),
|
||||
TypeFlag_IsCodeText = (1<<1),
|
||||
TypeFlag_IsPathText = (1<<2),
|
||||
};
|
||||
|
||||
typedef U32 MemberFlags;
|
||||
enum
|
||||
{
|
||||
MemberFlag_DoNotSerialize = (1<<0),
|
||||
};
|
||||
|
||||
typedef struct Type Type;
|
||||
typedef struct Member Member;
|
||||
struct Member
|
||||
{
|
||||
String8 name;
|
||||
String8 pretty_name;
|
||||
Type *type;
|
||||
U64 value;
|
||||
MemberFlags flags;
|
||||
};
|
||||
|
||||
typedef struct Type Type;
|
||||
struct Type
|
||||
{
|
||||
TypeKind kind;
|
||||
TypeFlags flags;
|
||||
U64 size;
|
||||
Type *direct;
|
||||
String8 name;
|
||||
String8 count_delimiter_name; // gathered from surrounding members, turns *->[1] into *->[N]
|
||||
U64 count;
|
||||
Member *members;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Type Serialization Parameters
|
||||
|
||||
typedef struct TypeSerializePtrRefInfo TypeSerializePtrRefInfo;
|
||||
struct TypeSerializePtrRefInfo
|
||||
{
|
||||
Type *type; // pointers to this
|
||||
void *indexify_base; // can be indexified using this
|
||||
void *offsetify_base; // can be offsetified using this
|
||||
void *nil_ptr; // is terminal if matching 0 or this
|
||||
};
|
||||
|
||||
typedef struct TypeSerializeParams TypeSerializeParams;
|
||||
struct TypeSerializeParams
|
||||
{
|
||||
U64 *advance_out;
|
||||
TypeSerializePtrRefInfo *ptr_ref_infos;
|
||||
U64 ptr_ref_infos_count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Type Name -> Type Info
|
||||
|
||||
#define type(T) (&T##__type)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Type Info Table Initializer Helpers
|
||||
|
||||
#define member_lit_comp(S, ti, m, ...) {str8_lit_comp(#m), {0}, (ti), OffsetOf(S, m), __VA_ARGS__}
|
||||
#define struct_members(S) read_only global Member S##__members[] =
|
||||
#define struct_type(S, ...) read_only global Type S##__type = {TypeKind_Struct, 0, sizeof(S), &type_nil, str8_lit_comp(#S), {0}, ArrayCount(S##__members), S##__members, __VA_ARGS__}
|
||||
#define named_struct_type(name, S, ...) read_only global Type name##__type = {TypeKind_Struct, 0, sizeof(S), &type_nil, str8_lit_comp(#name), {0}, ArrayCount(name##__members), name##__members, __VA_ARGS__}
|
||||
#define ptr_type(name, ti, ...) read_only global Type name = {TypeKind_Ptr, 0, sizeof(void *), (ti), __VA_ARGS__}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
read_only global Type type_nil = {TypeKind_Null, 0, 0, &type_nil};
|
||||
read_only global Member member_nil = {{0}, {0}, &type_nil};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Built-In Types
|
||||
|
||||
//- rjf: leaves
|
||||
read_only global Type void__type = {TypeKind_Void, 0, 0, &type_nil, str8_lit_comp("void")};
|
||||
read_only global Type U8__type = {TypeKind_U8, 0, sizeof(U8), &type_nil, str8_lit_comp("U8")};
|
||||
read_only global Type U16__type = {TypeKind_U16, 0, sizeof(U16), &type_nil, str8_lit_comp("U16")};
|
||||
read_only global Type U32__type = {TypeKind_U32, 0, sizeof(U32), &type_nil, str8_lit_comp("U32")};
|
||||
read_only global Type U64__type = {TypeKind_U64, 0, sizeof(U64), &type_nil, str8_lit_comp("U64")};
|
||||
read_only global Type S8__type = {TypeKind_S8, 0, sizeof(S8), &type_nil, str8_lit_comp("S8")};
|
||||
read_only global Type S16__type = {TypeKind_S16, 0, sizeof(S16), &type_nil, str8_lit_comp("S16")};
|
||||
read_only global Type S32__type = {TypeKind_S32, 0, sizeof(S32), &type_nil, str8_lit_comp("S32")};
|
||||
read_only global Type S64__type = {TypeKind_S64, 0, sizeof(S64), &type_nil, str8_lit_comp("S64")};
|
||||
read_only global Type B8__type = {TypeKind_B8, 0, sizeof(B8), &type_nil, str8_lit_comp("B8")};
|
||||
read_only global Type B16__type = {TypeKind_B16, 0, sizeof(B16), &type_nil, str8_lit_comp("B16")};
|
||||
read_only global Type B32__type = {TypeKind_B32, 0, sizeof(B32), &type_nil, str8_lit_comp("B32")};
|
||||
read_only global Type B64__type = {TypeKind_B64, 0, sizeof(B64), &type_nil, str8_lit_comp("B64")};
|
||||
read_only global Type F32__type = {TypeKind_F32, 0, sizeof(F32), &type_nil, str8_lit_comp("F32")};
|
||||
read_only global Type F64__type = {TypeKind_F64, 0, sizeof(F64), &type_nil, str8_lit_comp("F64")};
|
||||
read_only global Type *type_kind_type_table[] =
|
||||
{
|
||||
&type_nil,
|
||||
type(void),
|
||||
type(U8),
|
||||
type(U16),
|
||||
type(U32),
|
||||
type(U64),
|
||||
type(S8),
|
||||
type(S16),
|
||||
type(S32),
|
||||
type(S64),
|
||||
type(B8),
|
||||
type(B16),
|
||||
type(B32),
|
||||
type(B64),
|
||||
type(F32),
|
||||
type(F64),
|
||||
&type_nil,
|
||||
&type_nil,
|
||||
&type_nil,
|
||||
&type_nil,
|
||||
&type_nil,
|
||||
};
|
||||
|
||||
//- rjf: Rng1U64
|
||||
struct_members(Rng1U64)
|
||||
{
|
||||
member_lit_comp(Rng1U64, type(U64), min),
|
||||
member_lit_comp(Rng1U64, type(U64), max),
|
||||
};
|
||||
struct_type(Rng1U64);
|
||||
|
||||
//- rjf: String8
|
||||
ptr_type(String8__str_ptr_type, type(U8), str8_lit_comp("size"));
|
||||
struct_members(String8)
|
||||
{
|
||||
member_lit_comp(String8, &String8__str_ptr_type, str),
|
||||
member_lit_comp(String8, type(U64), size),
|
||||
};
|
||||
struct_type(String8);
|
||||
|
||||
//- rjf: String8Node
|
||||
extern Type String8Node__type;
|
||||
Type String8Node__ptr_type = {TypeKind_Ptr, 0, sizeof(void *), &String8Node__type};
|
||||
Member String8Node__members[] =
|
||||
{
|
||||
{str8_lit_comp("next"), {0}, &String8Node__ptr_type, OffsetOf(String8Node, next)},
|
||||
{str8_lit_comp("string"), {0}, type(String8), OffsetOf(String8Node, string)},
|
||||
};
|
||||
Type String8Node__type =
|
||||
{
|
||||
TypeKind_Struct,
|
||||
0,
|
||||
sizeof(String8Node),
|
||||
&type_nil,
|
||||
str8_lit_comp("String8Node"),
|
||||
{0},
|
||||
ArrayCount(String8Node__members),
|
||||
String8Node__members,
|
||||
};
|
||||
|
||||
//- rjf: String8List
|
||||
Member String8List__members[] =
|
||||
{
|
||||
{str8_lit_comp("first"), {0}, &String8Node__ptr_type, OffsetOf(String8List, first)},
|
||||
{str8_lit_comp("last"), {0}, &String8Node__ptr_type, OffsetOf(String8List, last), MemberFlag_DoNotSerialize},
|
||||
{str8_lit_comp("node_count"), {0}, type(U64), OffsetOf(String8List, node_count)},
|
||||
{str8_lit_comp("total_size"), {0}, type(U64), OffsetOf(String8List, total_size)},
|
||||
};
|
||||
Type String8List__type =
|
||||
{
|
||||
TypeKind_Struct,
|
||||
0,
|
||||
sizeof(String8List),
|
||||
&type_nil,
|
||||
str8_lit_comp("String8List"),
|
||||
{0},
|
||||
ArrayCount(String8List__members),
|
||||
String8List__members,
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Type Info Lookups
|
||||
|
||||
internal Member *member_from_name(Type *type, String8 name);
|
||||
#define EachMember(T, it) (Member *it = (type(T))->members; it != 0 && it < (type(T))->members + (type(T))->count; it += 1)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Type Info * Instance Operations
|
||||
|
||||
internal void typed_data_rebase_ptrs(Type *type, String8 data, void *base_ptr);
|
||||
internal String8 serialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
|
||||
internal String8 deserialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
|
||||
internal String8 deep_copy_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
|
||||
#define struct_rebase_ptrs(T, ptr, base) typed_data_rebase_ptrs(type(T), str8_struct(ptr), (base))
|
||||
#define serialized_from_struct(arena, T, ptr, ...) serialized_from_typed_data((arena), type(T), str8_struct(ptr), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__})
|
||||
#define struct_from_serialized(arena, T, string, ...) (T *)deserialized_from_typed_data((arena), type(T), (string), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__}).str
|
||||
#define deep_copy_from_struct(arena, T, ptr, ...) (T *)deep_copy_from_typed_data((arena), type(T), str8_struct(ptr), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__}).str
|
||||
|
||||
#endif // BASE_META_H
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Handle Type Functions
|
||||
|
||||
internal Process
|
||||
process_zero(void)
|
||||
{
|
||||
Process p = {0};
|
||||
return p;
|
||||
}
|
||||
|
||||
internal B32
|
||||
process_match(Process a, Process b)
|
||||
{
|
||||
B32 result = MemoryMatchStruct(&a, &b);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
process_list_push(Arena *arena, ProcessList *list, Process p)
|
||||
{
|
||||
ProcessNode *n = push_array(arena, ProcessNode, 1);
|
||||
SLLQueuePush(list->first, list->last, n);
|
||||
n->v = p;
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Process Launcher Helpers
|
||||
|
||||
internal Process
|
||||
launch_cmd_line(String8 string)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
U8 split_chars[] = {' '};
|
||||
String8List parts = str8_split(scratch.arena, string, split_chars, ArrayCount(split_chars), 0);
|
||||
Process process = {0};
|
||||
if(parts.node_count != 0)
|
||||
{
|
||||
// rjf: unpack exe part
|
||||
String8 exe = parts.first->string;
|
||||
String8 exe_folder = str8_chop_last_slash(exe);
|
||||
if(exe_folder.size == 0)
|
||||
{
|
||||
exe_folder = get_current_path(scratch.arena);
|
||||
}
|
||||
|
||||
// rjf: find stdout delimiter
|
||||
String8Node *stdout_delimiter_n = 0;
|
||||
for(String8Node *n = parts.first; n != 0; n = n->next)
|
||||
{
|
||||
if(str8_match(n->string, str8_lit(">"), 0))
|
||||
{
|
||||
stdout_delimiter_n = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: read stdout path
|
||||
String8 stdout_path = {0};
|
||||
if(stdout_delimiter_n && stdout_delimiter_n->next)
|
||||
{
|
||||
stdout_path = stdout_delimiter_n->next->string;
|
||||
}
|
||||
|
||||
// rjf: open stdout handle
|
||||
File stdout_handle = {0};
|
||||
if(stdout_path.size != 0)
|
||||
{
|
||||
File file = file_open(AccessFlag_Write|AccessFlag_Read, stdout_path);
|
||||
file_close(file);
|
||||
stdout_handle = file_open(AccessFlag_Write|AccessFlag_Append|AccessFlag_ShareRead|AccessFlag_ShareWrite|AccessFlag_Inherited, stdout_path);
|
||||
}
|
||||
|
||||
// rjf: form command line
|
||||
String8List cmdline = {0};
|
||||
for(String8Node *n = parts.first; n != stdout_delimiter_n && n != 0; n = n->next)
|
||||
{
|
||||
str8_list_push(scratch.arena, &cmdline, n->string);
|
||||
}
|
||||
|
||||
// rjf: launch
|
||||
ProcessLaunchParams params = {0};
|
||||
params.cmd_line = cmdline;
|
||||
params.path = exe_folder;
|
||||
params.inherit_env = 1;
|
||||
params.stdout_file = stdout_handle;
|
||||
process = process_launch(¶ms);
|
||||
|
||||
// rjf: close stdout handle
|
||||
{
|
||||
if(stdout_path.size != 0)
|
||||
{
|
||||
file_close(stdout_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return process;
|
||||
}
|
||||
|
||||
internal Process
|
||||
launch_cmd_linef(char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = str8fv(scratch.arena, fmt, args);
|
||||
Process result = launch_cmd_line(string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_PROCESSES_H
|
||||
#define BASE_PROCESSES_H
|
||||
|
||||
typedef struct ProcessInfo ProcessInfo;
|
||||
struct ProcessInfo
|
||||
{
|
||||
U32 pid;
|
||||
B32 large_pages_allowed;
|
||||
String8 binary_file_path;
|
||||
String8 binary_path;
|
||||
String8 initial_path;
|
||||
String8 user_program_config_data_path;
|
||||
String8 user_program_cache_data_path;
|
||||
String8 user_program_logs_data_path;
|
||||
String8List module_load_paths;
|
||||
String8List environment;
|
||||
};
|
||||
|
||||
typedef struct Process Process;
|
||||
struct Process
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct ProcessNode ProcessNode;
|
||||
struct ProcessNode
|
||||
{
|
||||
ProcessNode *next;
|
||||
Process v;
|
||||
};
|
||||
|
||||
typedef struct ProcessList ProcessList;
|
||||
struct ProcessList
|
||||
{
|
||||
ProcessNode *first;
|
||||
ProcessNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct ProcessLaunchParams ProcessLaunchParams;
|
||||
struct ProcessLaunchParams
|
||||
{
|
||||
String8List cmd_line;
|
||||
String8 path;
|
||||
String8List env;
|
||||
B32 inherit_env;
|
||||
B32 debug_subprocesses;
|
||||
B32 consoleless;
|
||||
File stdout_file;
|
||||
File stderr_file;
|
||||
File stdin_file;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Handle Type Functions
|
||||
|
||||
internal Process process_zero(void);
|
||||
internal B32 process_match(Process a, Process b);
|
||||
internal void process_list_push(Arena *arena, ProcessList *list, Process p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Process Launcher Helpers
|
||||
|
||||
internal Process launch_cmd_line(String8 string);
|
||||
internal Process launch_cmd_linef(char *fmt, ...);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Aborting
|
||||
|
||||
internal void abort_self(U64 exit_code);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Process Info
|
||||
|
||||
internal ProcessInfo *get_process_info(void);
|
||||
internal String8 get_current_path(Arena *arena);
|
||||
internal U32 get_process_start_time_unix(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Child Processes
|
||||
|
||||
internal Process process_launch(ProcessLaunchParams *params);
|
||||
internal U64 pid_from_process(Process process);
|
||||
internal B32 process_join(Process process, U64 endt_us, U64 *exit_code_out);
|
||||
internal void process_detach(Process process);
|
||||
internal B32 process_kill(Process process);
|
||||
|
||||
#endif // BASE_PROCESSES_H
|
||||
+2
-31
@@ -1,31 +1,2 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#if PROFILE_SPALL
|
||||
internal inline void
|
||||
spall_begin(char *fmt, ...)
|
||||
{
|
||||
if(spall_buffer.data == 0)
|
||||
{
|
||||
spall_buffer.length = MB(1);
|
||||
spall_buffer.data = os_reserve(spall_buffer.length);
|
||||
os_commit(spall_buffer.data, spall_buffer.length);
|
||||
spall_buffer_init(&spall_profile, &spall_buffer);
|
||||
}
|
||||
if(spall_pid == 0)
|
||||
{
|
||||
spall_pid = os_get_process_info()->pid;
|
||||
}
|
||||
if(spall_tid == 0)
|
||||
{
|
||||
spall_tid = os_tid();
|
||||
}
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
spall_buffer_begin_ex(&spall_profile, &spall_buffer, string.str, string.size, now_time_us(), spall_tid, spall_pid);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
#endif
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
+74
-126
@@ -1,126 +1,74 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_PROFILE_H
|
||||
#define BASE_PROFILE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Zero Settings
|
||||
|
||||
#if !defined(PROFILE_TELEMETRY)
|
||||
# define PROFILE_TELEMETRY 0
|
||||
#endif
|
||||
#if !defined(PROFILE_SPALL)
|
||||
# define PROFILE_SPALL 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Third Party Includes
|
||||
|
||||
#if PROFILE_TELEMETRY
|
||||
# include "rad_tm.h"
|
||||
# if OS_WINDOWS
|
||||
# pragma comment(lib, "ws2_32.lib")
|
||||
# pragma comment(lib, "rad_tm_win64.lib")
|
||||
# endif
|
||||
#elif PROFILE_SPALL
|
||||
# include "spall.h"
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Telemetry Profile Defines
|
||||
|
||||
#if PROFILE_TELEMETRY
|
||||
# define ProfBegin(...) tmEnter(0, 0, __VA_ARGS__)
|
||||
# define ProfBeginDynamic(...) (TM_API_PTR ? TM_API_PTR->_tmEnterZoneV_Core(0, 0, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0)
|
||||
# define ProfEnd(...) (TM_API_PTR ? TM_API_PTR->_tmLeaveZone(0) : (void)0)
|
||||
# define ProfTick(...) tmTick(0)
|
||||
# define ProfIsCapturing(...) tmRunning()
|
||||
# define ProfBeginCapture(...) tmOpen(0, __VA_ARGS__, __DATE__, "localhost", TMCT_TCP, TELEMETRY_DEFAULT_PORT, TMOF_INIT_NETWORKING|TMOF_CAPTURE_CONTEXT_SWITCHES, 100)
|
||||
# define ProfEndCapture(...) tmClose(0)
|
||||
# define ProfThreadName(...) (TM_API_PTR ? TM_API_PTR->_tmThreadName(0, 0, __VA_ARGS__) : (void)0)
|
||||
# define ProfMsg(...) (TM_API_PTR ? TM_API_PTR->_tmMessageV_Core(0, TMMF_ICON_NOTE, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0)
|
||||
# define ProfBeginLockWait(...) tmStartWaitForLock(0, 0, __VA_ARGS__)
|
||||
# define ProfEndLockWait(...) tmEndWaitForLock(0)
|
||||
# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__)
|
||||
# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__)
|
||||
# define ProfColor(color) tmZoneColor((((color) & 0xff000000) >> 24) / 255.f, (((color) & 0x00ff0000) >> 16) / 255.f, (((color) & 0x0000ff00) >> 8) / 255.f)
|
||||
# define ProfBeginV(...) \
|
||||
if (TM_API_PTR) { \
|
||||
static tm_uint64 file_id = 0; TM_API_PTR->_tmStaticString(&file_id, __FILE__); \
|
||||
Temp scratch = scratch_begin(0,0); \
|
||||
String8 string = push_str8f(scratch.arena, __VA_ARGS__); \
|
||||
tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \
|
||||
hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \
|
||||
TM_API_PTR->_tmEnterZoneFast_Core(0, 0, file_id, __LINE__, hash); \
|
||||
scratch_end(scratch); \
|
||||
}
|
||||
# define ProfNoteV(...) \
|
||||
if (TM_API_PTR) { \
|
||||
static tm_uint64 file_id = 0; TM_API_PTR->_tmStaticString(&file_id, __FILE__); \
|
||||
Temp scratch = scratch_begin(0,0); \
|
||||
String8 string = push_str8f(scratch.arena, __VA_ARGS__); \
|
||||
tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \
|
||||
hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \
|
||||
TM_API_PTR->_tmMessageFast_Core(0, TMMF_ICON_NOTE, file_id, __LINE__, hash); \
|
||||
scratch_end(scratch); \
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Spall Profile Defines
|
||||
|
||||
#if PROFILE_SPALL
|
||||
global U64 spall_capturing = 0;
|
||||
global SpallProfile spall_profile = {0};
|
||||
thread_static SpallBuffer spall_buffer = {0};
|
||||
thread_static U32 spall_tid = 0;
|
||||
thread_static U32 spall_pid = 0;
|
||||
internal inline void spall_begin(char *fmt, ...);
|
||||
# define ProfBegin(...) (spall_capturing ? (spall_begin(__VA_ARGS__), 0) : 0)
|
||||
# define ProfBeginDynamic(...) (spall_capturing ? (spall_begin(__VA_ARGS__), 0) : 0)
|
||||
# define ProfEnd(...) (spall_capturing ? (spall_buffer_end_ex(&spall_profile, &spall_buffer, now_time_us(), spall_tid, spall_pid)), 0 : 0)
|
||||
# define ProfTick(...)
|
||||
# define ProfIsCapturing(...) (!!spall_capturing)
|
||||
# define ProfBeginCapture(...) (spall_capturing = 1)
|
||||
# define ProfEndCapture(...) (spall_capturing = 0, spall_flush(&spall_profile))
|
||||
# define ProfThreadName(...)
|
||||
# define ProfMsg(...)
|
||||
# define ProfBeginLockWait(...)
|
||||
# define ProfEndLockWait(...)
|
||||
# define ProfLockTake(...)
|
||||
# define ProfLockDrop(...)
|
||||
# define ProfColor(color)
|
||||
# define ProfBeginV(...)
|
||||
# define ProfNoteV(...)
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Zeroify Undefined Defines
|
||||
|
||||
#if !defined(ProfBegin)
|
||||
# define ProfBegin(...) (0)
|
||||
# define ProfBeginDynamic(...) (0)
|
||||
# define ProfEnd(...) (0)
|
||||
# define ProfTick(...) (0)
|
||||
# define ProfIsCapturing(...) (0)
|
||||
# define ProfBeginCapture(...) (0)
|
||||
# define ProfEndCapture(...) (0)
|
||||
# define ProfThreadName(...) (0)
|
||||
# define ProfMsg(...) (0)
|
||||
# define ProfBeginLockWait(...) (0)
|
||||
# define ProfEndLockWait(...) (0)
|
||||
# define ProfLockTake(...) (0)
|
||||
# define ProfLockDrop(...) (0)
|
||||
# define ProfColor(...) (0)
|
||||
# define ProfBeginV(...) (0)
|
||||
# define ProfNoteV(...) (0)
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Helper Wrappers
|
||||
|
||||
#define ProfBeginFunction(...) ProfBegin(this_function_name)
|
||||
#define ProfScope(...) DeferLoop(ProfBeginDynamic(__VA_ARGS__), ProfEnd())
|
||||
|
||||
#endif // BASE_PROFILE_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_PROFILE_H
|
||||
#define BASE_PROFILE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Zero Settings
|
||||
|
||||
#if !defined(PROFILE_TELEMETRY)
|
||||
# define PROFILE_TELEMETRY 0
|
||||
#endif
|
||||
|
||||
#if !defined(MARKUP_LAYER_COLOR)
|
||||
# define MARKUP_LAYER_COLOR 1.00f, 0.00f, 1.00f
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Third Party Includes
|
||||
|
||||
#if PROFILE_TELEMETRY
|
||||
# include "rad_tm.h"
|
||||
# if OS_WINDOWS
|
||||
# pragma comment(lib, "rad_tm_win64.lib")
|
||||
# endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Telemetry Profile Defines
|
||||
|
||||
#if PROFILE_TELEMETRY
|
||||
# define ProfBegin(...) tmEnter(0, 0, __VA_ARGS__)
|
||||
# define ProfBeginDynamic(...) (TM_API_PTR ? TM_API_PTR->_tmEnterZoneV_Core(0, 0, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0)
|
||||
# define ProfEnd(...) (TM_API_PTR ? TM_API_PTR->_tmLeaveZone(0) : (void)0)
|
||||
# define ProfTick(...) tmTick(0)
|
||||
# define ProfIsCapturing(...) tmRunning()
|
||||
# define ProfBeginCapture(...) tmOpen(0, __VA_ARGS__, __DATE__, "localhost", TMCT_TCP, TELEMETRY_DEFAULT_PORT, TMOF_INIT_NETWORKING|TMOF_CAPTURE_CONTEXT_SWITCHES, 100)
|
||||
# define ProfEndCapture(...) tmClose(0)
|
||||
# define ProfThreadName(...) (TM_API_PTR ? TM_API_PTR->_tmThreadName(0, 0, __VA_ARGS__) : (void)0)
|
||||
# define ProfMsg(...) (TM_API_PTR ? TM_API_PTR->_tmMessageV_Core(0, TMMF_ICON_NOTE, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0)
|
||||
# define ProfBeginLockWait(...) tmStartWaitForLock(0, 0, __VA_ARGS__)
|
||||
# define ProfEndLockWait(...) tmEndWaitForLock(0)
|
||||
# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__)
|
||||
# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__)
|
||||
# define ProfColor(color) tmZoneColorSticky(color)
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Zeroify Undefined Defines
|
||||
|
||||
#if !defined(ProfBegin)
|
||||
# define ProfBegin(...) (0)
|
||||
# define ProfBeginDynamic(...) (0)
|
||||
# define ProfEnd(...) (0)
|
||||
# define ProfTick(...) (0)
|
||||
# define ProfIsCapturing(...) (0)
|
||||
# define ProfBeginCapture(...) (0)
|
||||
# define ProfEndCapture(...) (0)
|
||||
# define ProfThreadName(...) (0)
|
||||
# define ProfMsg(...) (0)
|
||||
# define ProfBeginLockWait(...) (0)
|
||||
# define ProfEndLockWait(...) (0)
|
||||
# define ProfLockTake(...) (0)
|
||||
# define ProfLockDrop(...) (0)
|
||||
# define ProfColor(...) (0)
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Helper Wrappers
|
||||
|
||||
#define ProfBeginFunction(...) ProfBegin(this_function_name)
|
||||
#define ProfScope(...) DeferLoop(ProfBeginDynamic(__VA_ARGS__), ProfEnd())
|
||||
|
||||
#endif // BASE_PROFILE_H
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Ring Functions
|
||||
|
||||
internal Ring *
|
||||
make_ring(Arena *arena, U64 size)
|
||||
{
|
||||
Ring *ring = push_array(arena, Ring, 1);
|
||||
ring->size = size;
|
||||
ring->base = push_array(arena, U8, ring->size);
|
||||
return ring;
|
||||
}
|
||||
|
||||
internal B32
|
||||
ring_try_write(Ring *ring, U64 size, void *ptr)
|
||||
{
|
||||
U64 bytes_unconsumed = (ring->write_pos - ring->read_pos);
|
||||
U64 bytes_available = ring->size - bytes_unconsumed;
|
||||
B32 result = 0;
|
||||
if(bytes_available >= size)
|
||||
{
|
||||
result = 1;
|
||||
ring->write_pos += wrapped_write(ring->base, ring->size, ring->write_pos, ptr, size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
ring_try_read(Ring *ring, U64 size, void *ptr)
|
||||
{
|
||||
U64 bytes_unconsumed = (ring->write_pos - ring->read_pos);
|
||||
B32 result = 0;
|
||||
if(bytes_unconsumed >= size)
|
||||
{
|
||||
result = 1;
|
||||
ring->read_pos += wrapped_read(ring->base, ring->size, ring->read_pos, ptr, size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Guarded Ring Functions
|
||||
|
||||
internal GuardedRing *
|
||||
guarded_ring_alloc(Arena *arena, U64 size)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
GuardedRing *gr = push_array(arena, GuardedRing, 1);
|
||||
gr->ring = make_ring(arena, size);
|
||||
gr->mutex = mutex_alloc();
|
||||
gr->cv = cond_var_alloc();
|
||||
ProfEnd();
|
||||
return gr;
|
||||
}
|
||||
|
||||
internal void
|
||||
guarded_ring_release(GuardedRing *ring)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
mutex_release(ring->mutex);
|
||||
cond_var_release(ring->cv);
|
||||
ProfEnd();
|
||||
}
|
||||
|
||||
internal RingGuard
|
||||
guarded_ring_open(GuardedRing *ring)
|
||||
{
|
||||
RingGuard guard = {ring};
|
||||
mutex_take(ring->mutex);
|
||||
return guard;
|
||||
}
|
||||
|
||||
internal void
|
||||
guarded_ring_close(RingGuard *guard)
|
||||
{
|
||||
mutex_drop(guard->r->mutex);
|
||||
}
|
||||
|
||||
internal B32
|
||||
guarded_ring_try_write(RingGuard *guard, U64 size, void *ptr)
|
||||
{
|
||||
B32 result = ring_try_write(guard->r->ring, size, ptr);
|
||||
if(result)
|
||||
{
|
||||
cond_var_broadcast(guard->r->cv);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
guarded_ring_try_read(RingGuard *guard, U64 size, void *ptr)
|
||||
{
|
||||
B32 result = ring_try_read(guard->r->ring, size, ptr);
|
||||
if(result)
|
||||
{
|
||||
cond_var_broadcast(guard->r->cv);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
guarded_ring_write_or_wait(RingGuard *guard, U64 size, void *ptr, U64 endt_us)
|
||||
{
|
||||
B32 write_good = 0;
|
||||
for(;!write_good;)
|
||||
{
|
||||
write_good = guarded_ring_try_write(guard, size, ptr);
|
||||
if(now_time_us() >= endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(!write_good)
|
||||
{
|
||||
cond_var_wait(guard->r->cv, guard->r->mutex, endt_us);
|
||||
}
|
||||
}
|
||||
return write_good;
|
||||
}
|
||||
|
||||
internal B32
|
||||
guarded_ring_read_or_wait(RingGuard *guard, U64 size, void *ptr, U64 endt_us)
|
||||
{
|
||||
B32 read_good = 0;
|
||||
for(;!read_good;)
|
||||
{
|
||||
read_good = guarded_ring_try_read(guard, size, ptr);
|
||||
if(now_time_us() >= endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(!read_good)
|
||||
{
|
||||
cond_var_wait(guard->r->cv, guard->r->mutex, endt_us);
|
||||
}
|
||||
}
|
||||
return read_good;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_RING_H
|
||||
#define BASE_RING_H
|
||||
|
||||
typedef struct Ring Ring;
|
||||
struct Ring
|
||||
{
|
||||
U8 *base;
|
||||
U64 size;
|
||||
U64 write_pos;
|
||||
U64 read_pos;
|
||||
};
|
||||
|
||||
typedef struct GuardedRing GuardedRing;
|
||||
struct GuardedRing
|
||||
{
|
||||
Ring *ring;
|
||||
Mutex mutex;
|
||||
CondVar cv;
|
||||
};
|
||||
|
||||
typedef struct RingGuard RingGuard;
|
||||
struct RingGuard
|
||||
{
|
||||
GuardedRing *r;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Ring Functions
|
||||
|
||||
internal Ring *make_ring(Arena *arena, U64 size);
|
||||
internal B32 ring_try_write(Ring *ring, U64 size, void *ptr);
|
||||
internal B32 ring_try_read(Ring *ring, U64 size, void *ptr);
|
||||
#define ring_try_write_struct(ring, ptr) ring_try_write((ring), sizeof(*(ptr)), (ptr))
|
||||
#define ring_try_read_struct(ring, ptr) ring_try_read((ring), sizeof(*(ptr)), (ptr))
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Guarded Ring Functions
|
||||
|
||||
internal GuardedRing *guarded_ring_alloc(Arena *arena, U64 size);
|
||||
internal void guarded_ring_release(GuardedRing *ring);
|
||||
internal RingGuard guarded_ring_open(GuardedRing *ring);
|
||||
internal void guarded_ring_close(RingGuard *guard);
|
||||
internal B32 guarded_ring_try_write(RingGuard *guard, U64 size, void *ptr);
|
||||
internal B32 guarded_ring_try_read(RingGuard *guard, U64 size, void *ptr);
|
||||
#define guarded_ring_try_write_struct(ring, ptr) guarded_ring_try_write((ring), sizeof(*(ptr)), (ptr))
|
||||
#define guarded_ring_try_read_struct(ring, ptr) guarded_ring_try_read((ring), sizeof(*(ptr)), (ptr))
|
||||
internal B32 guarded_ring_write_or_wait(RingGuard *guard, U64 size, void *ptr, U64 endt_us);
|
||||
internal B32 guarded_ring_read_or_wait(RingGuard *guard, U64 size, void *ptr, U64 endt_us);
|
||||
#define guarded_ring_write_struct_or_wait(ring, ptr, endt_us) guarded_ring_write_or_wait((ring), sizeof(*(ptr)), (ptr), (endt_us))
|
||||
#define guarded_ring_read_struct_or_wait(ring, ptr, endt_us) guarded_ring_read_or_wait((ring), sizeof(*(ptr)), (ptr), (endt_us))
|
||||
#define guarded_ring_write_string_or_wait(ring, string, endt_us) guarded_ring_write_or_wait((ring), (string).size, (string).str, (endt_us))
|
||||
|
||||
#endif // BASE_RING_H
|
||||
@@ -1,2 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_SHARED_MEMORY_H
|
||||
#define BASE_SHARED_MEMORY_H
|
||||
|
||||
typedef struct SharedMemory SharedMemory;
|
||||
struct SharedMemory
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Shared Memory
|
||||
|
||||
internal SharedMemory shared_memory_alloc(U64 size, String8 name);
|
||||
internal SharedMemory shared_memory_open(String8 name);
|
||||
internal void shared_memory_close(SharedMemory handle);
|
||||
internal void * shared_memory_view_open(SharedMemory handle, Rng1U64 range);
|
||||
internal void shared_memory_view_close(SharedMemory handle, void *ptr, Rng1U64 range);
|
||||
|
||||
#endif // BASE_SHARED_MEMORY_H
|
||||
+1937
-3276
File diff suppressed because it is too large
Load Diff
+376
-480
@@ -1,480 +1,376 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_STRINGS_H
|
||||
#define BASE_STRINGS_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Types
|
||||
|
||||
typedef struct String8 String8;
|
||||
struct String8
|
||||
{
|
||||
U8 *str;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
typedef struct String16 String16;
|
||||
struct String16
|
||||
{
|
||||
U16 *str;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
typedef struct String32 String32;
|
||||
struct String32
|
||||
{
|
||||
U32 *str;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String List & Array Types
|
||||
|
||||
typedef struct String8Node String8Node;
|
||||
struct String8Node
|
||||
{
|
||||
String8Node *next;
|
||||
String8 string;
|
||||
};
|
||||
|
||||
typedef struct String8List String8List;
|
||||
struct String8List
|
||||
{
|
||||
String8Node *first;
|
||||
String8Node *last;
|
||||
U64 node_count;
|
||||
U64 total_size;
|
||||
};
|
||||
|
||||
typedef struct String8Array String8Array;
|
||||
struct String8Array
|
||||
{
|
||||
String8 *v;
|
||||
U64 count;
|
||||
U64 total_size;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Matching, Splitting, & Joining Types
|
||||
|
||||
typedef U32 StringMatchFlags;
|
||||
enum
|
||||
{
|
||||
StringMatchFlag_CaseInsensitive = (1 << 0),
|
||||
StringMatchFlag_RightSideSloppy = (1 << 1),
|
||||
StringMatchFlag_SlashInsensitive = (1 << 2),
|
||||
};
|
||||
|
||||
typedef U32 StringSplitFlags;
|
||||
enum
|
||||
{
|
||||
StringSplitFlag_KeepEmpties = (1 << 0),
|
||||
};
|
||||
|
||||
typedef enum PathStyle
|
||||
{
|
||||
PathStyle_Null,
|
||||
PathStyle_Relative,
|
||||
PathStyle_WindowsAbsolute,
|
||||
PathStyle_UnixAbsolute,
|
||||
|
||||
#if OS_WINDOWS
|
||||
PathStyle_SystemAbsolute = PathStyle_WindowsAbsolute
|
||||
#elif OS_LINUX
|
||||
PathStyle_SystemAbsolute = PathStyle_UnixAbsolute
|
||||
#else
|
||||
# error Absolute path style is undefined for this OS.
|
||||
#endif
|
||||
}
|
||||
PathStyle;
|
||||
|
||||
typedef struct StringJoin StringJoin;
|
||||
struct StringJoin
|
||||
{
|
||||
String8 pre;
|
||||
String8 sep;
|
||||
String8 post;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Pair Types
|
||||
|
||||
typedef struct String8TxtPtPair String8TxtPtPair;
|
||||
struct String8TxtPtPair
|
||||
{
|
||||
String8 string;
|
||||
TxtPt pt;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: UTF Decoding Types
|
||||
|
||||
typedef struct UnicodeDecode UnicodeDecode;
|
||||
struct UnicodeDecode
|
||||
{
|
||||
U32 inc;
|
||||
U32 codepoint;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Fuzzy Matching Types
|
||||
|
||||
typedef struct FuzzyMatchRangeNode FuzzyMatchRangeNode;
|
||||
struct FuzzyMatchRangeNode
|
||||
{
|
||||
FuzzyMatchRangeNode *next;
|
||||
Rng1U64 range;
|
||||
};
|
||||
|
||||
typedef struct FuzzyMatchRangeList FuzzyMatchRangeList;
|
||||
struct FuzzyMatchRangeList
|
||||
{
|
||||
FuzzyMatchRangeNode *first;
|
||||
FuzzyMatchRangeNode *last;
|
||||
U64 count;
|
||||
U64 needle_part_count;
|
||||
U64 total_dim;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Character Classification & Conversion Functions
|
||||
|
||||
internal B32 char_is_space(U8 c);
|
||||
internal B32 char_is_upper(U8 c);
|
||||
internal B32 char_is_lower(U8 c);
|
||||
internal B32 char_is_alpha(U8 c);
|
||||
internal B32 char_is_slash(U8 c);
|
||||
internal B32 char_is_digit(U8 c, U32 base);
|
||||
internal U8 lower_from_char(U8 c);
|
||||
internal U8 upper_from_char(U8 c);
|
||||
internal U8 correct_slash_from_char(U8 c);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: C-String Measurement
|
||||
|
||||
internal U64 cstring8_length(U8 *c);
|
||||
internal U64 cstring16_length(U16 *c);
|
||||
internal U64 cstring32_length(U32 *c);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Constructors
|
||||
|
||||
#define s(S) str8_lit(S)
|
||||
|
||||
#define str8_lit(S) str8((U8*)(S), sizeof(S) - 1)
|
||||
#define str8_lit_comp(S) {(U8*)(S), sizeof(S) - 1,}
|
||||
#define str8_lit_cstr(S) str8((U8*)(S), sizeof(S))
|
||||
#define str8_varg(S) (int)((S).size), ((S).str)
|
||||
|
||||
#define str8_array(S,C) str8((U8*)(S), sizeof(*(S))*(C))
|
||||
#define str8_array_fixed(S) str8((U8*)(S), sizeof(S))
|
||||
#define str8_struct(S) str8((U8*)(S), sizeof(*(S)))
|
||||
|
||||
internal String8 str8(U8 *str, U64 size);
|
||||
internal String8 str8_range(U8 *first, U8 *one_past_last);
|
||||
internal String8 str8_zero(void);
|
||||
internal String16 str16(U16 *str, U64 size);
|
||||
internal String16 str16_range(U16 *first, U16 *one_past_last);
|
||||
internal String16 str16_zero(void);
|
||||
internal String32 str32(U32 *str, U64 size);
|
||||
internal String32 str32_range(U32 *first, U32 *one_past_last);
|
||||
internal String32 str32_zero(void);
|
||||
internal String8 str8_cstring(char *c);
|
||||
internal String16 str16_cstring(U16 *c);
|
||||
internal String32 str32_cstring(U32 *c);
|
||||
internal String8 str8_cstring_capped(void *cstr, void *cap);
|
||||
internal String16 str16_cstring_capped(void *cstr, void *cap);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Stylization
|
||||
|
||||
internal String8 upper_from_str8(Arena *arena, String8 string);
|
||||
internal String8 lower_from_str8(Arena *arena, String8 string);
|
||||
internal String8 backslashed_from_str8(Arena *arena, String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Matching
|
||||
|
||||
#define str8_match_lit(a_lit, b, flags) str8_match(str8_lit(a_lit), (b), (flags))
|
||||
#define str8_match_cstr(a_cstr, b, flags) str8_match(str8_cstring(a_cstr), (b), (flags))
|
||||
internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags);
|
||||
#define str8_matchi(a, b) str8_match(a, b, StringMatchFlag_CaseInsensitive)
|
||||
internal B32 str8_match_wildcard(String8 string, String8 pattern, StringMatchFlags flags);
|
||||
internal B32 str8_starts_with(String8 string, String8 expected_prefix);
|
||||
internal B32 str8_starts_withi(String8 string, String8 expected_prefix);
|
||||
internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
|
||||
internal U64 str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
|
||||
internal B32 str8_is_before(String8 a, String8 b);
|
||||
#define str8_ends_with(string, end, flags) str8_match(str8_postfix((string), (end).size), (end), (flags))
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Slicing
|
||||
|
||||
internal String8 str8_substr(String8 str, Rng1U64 range);
|
||||
internal String8 str8_prefix(String8 str, U64 size);
|
||||
internal String8 str8_skip(String8 str, U64 amt);
|
||||
internal String8 str8_postfix(String8 str, U64 size);
|
||||
internal String8 str8_chop(String8 str, U64 amt);
|
||||
internal String8 str8_chop_line(String8 *str);
|
||||
internal String8 str8_skip_chop_whitespace(String8 string);
|
||||
internal String8 str8_skip_chop_slashes(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Formatting & Copying
|
||||
|
||||
internal String8 str8_cat(Arena *arena, String8 s1, String8 s2);
|
||||
internal String8 str8_copy(Arena *arena, String8 s);
|
||||
internal String8 str8fv(Arena *arena, char *fmt, va_list args);
|
||||
internal String8 str8f(Arena *arena, char *fmt, ...);
|
||||
// TODO(rjf): remove these once we're ready to convert all usages:
|
||||
#define push_str8_cat(arena, s1, s2) str8_cat((arena), (s1), (s2))
|
||||
#define push_str8_copy(arena, s) str8_copy((arena), (s))
|
||||
#define push_str8fv(arena, fmt, args) str8fv((arena), (fmt), (args))
|
||||
#define push_str8f(arena, ...) str8f((arena), __VA_ARGS__)
|
||||
internal String8 push_cstr(Arena *arena, String8 str); // TODO(rjf): this is unnecessary - this is implied by `push_str8_copy`. need to remove.
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <=> Integer Conversions
|
||||
|
||||
//- rjf: string -> integer
|
||||
internal S64 sign_from_str8(String8 string, String8 *string_tail);
|
||||
internal B32 str8_is_integer(String8 string, U32 radix);
|
||||
internal B32 str8_is_integer_signed(String8 string, U32 radix);
|
||||
internal U64 u64_from_str8(String8 string, U32 radix);
|
||||
internal S64 s64_from_str8(String8 string, U32 radix);
|
||||
internal U32 u32_from_str8(String8 string, U32 radix);
|
||||
internal S32 s32_from_str8(String8 string, U32 radix);
|
||||
internal B32 try_u64_from_str8_c_rules(String8 string, U64 *x);
|
||||
internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x);
|
||||
|
||||
//- rjf: integer -> string
|
||||
internal String8 str8_from_memory_size(Arena *arena, U64 size);
|
||||
internal String8 str8_from_count(Arena *arena, U64 count);
|
||||
internal String8 str8_from_bits_u32(Arena *arena, U32 x);
|
||||
internal String8 str8_from_bits_u64(Arena *arena, U64 x);
|
||||
internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator);
|
||||
internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <=> Float Conversions
|
||||
|
||||
internal F64 f64_from_str8(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String List Construction Functions
|
||||
|
||||
internal String8Node *str8_list_push_node(String8List *list, String8Node *node);
|
||||
internal String8Node *str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string);
|
||||
internal String8Node *str8_list_push_node_front(String8List *list, String8Node *node);
|
||||
internal String8Node *str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string);
|
||||
internal String8Node *str8_list_push(Arena *arena, String8List *list, String8 string);
|
||||
internal String8Node *str8_list_push_front(Arena *arena, String8List *list, String8 string);
|
||||
internal void str8_list_concat_in_place(String8List *list, String8List *to_push);
|
||||
internal String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align);
|
||||
internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...);
|
||||
internal String8Node* str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...);
|
||||
internal String8Node* str8_list_pop_front(String8List *list);
|
||||
internal String8List str8_list_copy(Arena *arena, String8List *list);
|
||||
internal String8List str8_list_substr(Arena *arena, String8List list, Rng1U64 range);
|
||||
#define str8_list_first(list) ((list)->first ? (list)->first->string : str8_zero())
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Splitting & Joining
|
||||
|
||||
internal String8List str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags);
|
||||
internal String8List str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags);
|
||||
internal String8 str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Data Stringification Helpers
|
||||
|
||||
internal String8List numeric_str8_list_from_data(Arena *arena, U32 radix, String8 data, U64 stride);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf; String Arrays
|
||||
|
||||
internal String8Array str8_array_zero(void);
|
||||
internal String8Array str8_array_from_list(Arena *arena, String8List *list);
|
||||
internal String8Array str8_array_reserve(Arena *arena, U64 count);
|
||||
internal String8Array str8_array_copy(Arena *arena, String8Array array);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <-> Version Helpers
|
||||
|
||||
internal U64 version_from_str8(String8 string);
|
||||
internal String8 str8_from_version(Arena *arena, U64 version);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Path Helpers
|
||||
|
||||
global read_only struct
|
||||
{
|
||||
String8 string;
|
||||
PathStyle path_style;
|
||||
}
|
||||
g_path_style_map[] =
|
||||
{
|
||||
{ str8_lit_comp(""), PathStyle_Null },
|
||||
{ str8_lit_comp("relative"), PathStyle_Relative },
|
||||
{ str8_lit_comp("windows"), PathStyle_WindowsAbsolute },
|
||||
{ str8_lit_comp("unix"), PathStyle_UnixAbsolute },
|
||||
{ str8_lit_comp("system"), PathStyle_SystemAbsolute },
|
||||
};
|
||||
|
||||
internal String8 program_ext_postfix_from_os(OperatingSystem os, B32 require_ext);
|
||||
|
||||
internal String8 str8_chop_last_slash(String8 string);
|
||||
internal String8 str8_skip_last_slash(String8 string);
|
||||
internal String8 str8_chop_last_dot(String8 string);
|
||||
internal String8 str8_skip_last_dot(String8 string);
|
||||
|
||||
internal PathStyle path_style_from_str8(String8 string);
|
||||
internal String8List str8_split_path(Arena *arena, String8 string);
|
||||
internal void str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style);
|
||||
internal String8 str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style);
|
||||
|
||||
internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Relative <-> Absolute Path
|
||||
|
||||
internal String8 path_relative_dst_from_absolute_dst_src(Arena *arena, String8 dst, String8 src);
|
||||
internal String8 path_absolute_dst_from_relative_dst_src(Arena *arena, String8 dst, String8 src);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Path Normalization
|
||||
|
||||
internal String8List path_normalized_list_from_string(Arena *arena, String8 path, PathStyle *style_out);
|
||||
internal String8 path_normalized_from_string(Arena *arena, String8 path);
|
||||
internal B32 path_match_normalized(String8 left, String8 right);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Misc. Path Helpers
|
||||
|
||||
internal PathStyle path_style_from_string(String8 string);
|
||||
internal String8 string_from_path_style(PathStyle style);
|
||||
internal String8 path_separator_string_from_style(PathStyle style);
|
||||
internal StringMatchFlags path_match_flags_from_os(OperatingSystem os);
|
||||
internal String8 path_convert_slashes(Arena *arena, String8 path, PathStyle path_style);
|
||||
internal String8 path_replace_file_extension(Arena *arena, String8 file_name, String8 ext);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: UTF-8 & UTF-16 Decoding/Encoding
|
||||
|
||||
internal UnicodeDecode utf8_decode(U8 *str, U64 max);
|
||||
internal UnicodeDecode utf16_decode(U16 *str, U64 max);
|
||||
internal U32 utf8_encode(U8 *str, U32 codepoint);
|
||||
internal U32 utf16_encode(U16 *str, U32 codepoint);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Unicode String Conversions
|
||||
|
||||
internal String8 str8_from_16(Arena *arena, String16 in);
|
||||
internal String16 str16_from_8(Arena *arena, String8 in);
|
||||
internal String8 str8_from_32(Arena *arena, String32 in);
|
||||
internal String32 str32_from_8(Arena *arena, String8 in);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Types & Space Enum <-> String Conversions
|
||||
|
||||
internal OperatingSystem operating_system_from_string(String8 string);
|
||||
internal String8 string_from_dimension(Dimension dimension);
|
||||
internal String8 string_from_side(Side side);
|
||||
internal String8 string_from_operating_system(OperatingSystem os);
|
||||
internal String8 string_from_arch(Arch arch);
|
||||
internal String8 string_from_week_day(WeekDay week_day);
|
||||
internal String8 string_from_month(Month month);
|
||||
internal String8 string_from_date_time(Arena *arena, DateTime *date_time);
|
||||
internal String8 string_from_date_time__file_name(Arena *arena, DateTime *date_time);
|
||||
internal String8 string_from_elapsed_time(Arena *arena, DateTime dt);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <-> Globally Unique IDs
|
||||
|
||||
internal String8 string_from_guid(Arena *arena, Guid guid);
|
||||
internal B32 try_guid_from_string(String8 string, Guid *guid_out);
|
||||
internal Guid guid_from_string(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Text Indentation
|
||||
|
||||
internal String8 indented_from_string(Arena *arena, String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Text Escaping
|
||||
|
||||
internal String8 escaped_from_raw_str8(Arena *arena, String8 string);
|
||||
internal String8 raw_from_escaped_str8(Arena *arena, String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Text Wrapping
|
||||
|
||||
internal String8List wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <-> Color
|
||||
|
||||
internal String8 hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba);
|
||||
internal Vec4F32 rgba_from_hex_string_4f32(String8 hex_string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Fuzzy Matching
|
||||
|
||||
internal FuzzyMatchRangeList fuzzy_match_find(Arena *arena, String8 needle, String8 haystack);
|
||||
internal FuzzyMatchRangeList fuzzy_match_range_list_copy(Arena *arena, FuzzyMatchRangeList *src);
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(allen): Serialization Helpers
|
||||
|
||||
internal void str8_serial_begin(Arena *arena, String8List *srl);
|
||||
internal String8 str8_serial_end(Arena *arena, String8List *srl);
|
||||
internal void str8_serial_write_to_dst(String8List *srl, void *out);
|
||||
internal U64 str8_serial_push_align(Arena *arena, String8List *srl, U64 align);
|
||||
internal void * str8_serial_push_size(Arena *arena, String8List *srl, U64 size);
|
||||
internal void * str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size);
|
||||
internal void str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first);
|
||||
internal void * str8_serial_push_u64(Arena *arena, String8List *srl, U64 x);
|
||||
internal void * str8_serial_push_u32(Arena *arena, String8List *srl, U32 x);
|
||||
internal void * str8_serial_push_u16(Arena *arena, String8List *srl, U16 x);
|
||||
internal void * str8_serial_push_u8(Arena *arena, String8List *srl, U8 x);
|
||||
internal void * str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str);
|
||||
internal void * str8_serial_push_string(Arena *arena, String8List *srl, String8 str);
|
||||
#define str8_serial_push_array(arena, srl, ptr, count) str8_serial_push_data(arena, srl, ptr, sizeof(*(ptr)) * (count))
|
||||
#define str8_serial_push_struct(arena, srl, ptr) str8_serial_push_array(arena, srl, ptr, 1)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Deserialization Helpers
|
||||
|
||||
internal U64 str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity);
|
||||
internal U64 str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val);
|
||||
internal void * str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size);
|
||||
internal U64 str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out);
|
||||
internal U64 str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out);
|
||||
internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out);
|
||||
#define str8_deserial_read_array(string, off, ptr, count) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr))*(count), sizeof(*(ptr)))
|
||||
#define str8_deserial_read_struct(string, off, ptr) str8_deserial_read_array(string, off, ptr, 1)
|
||||
internal U64 str8_deserial_read_uleb128(String8 string, U64 off, U64 *value_out);
|
||||
internal U64 str8_deserial_read_sleb128(String8 string, U64 off, S64 *value_out);
|
||||
|
||||
////////////////////////////////
|
||||
//~ string buffer
|
||||
|
||||
internal B32 str8_buffer_skip(String8Node *buf, U64 *pos, U64 skip);
|
||||
internal U64 str8_buffer_read(String8Node *buf, U64 *pos, U64 read_size, void *out);
|
||||
internal U64 str8_buffer_peek(String8Node *buf, U64 *pos, U64 read_size, void *out);
|
||||
internal U64 str8_buffer_write(String8Node *buf, U64 *pos, String8 data);
|
||||
internal U64 str8_buffer_write_u16(String8Node *buf, U64 *pos, U16 v);
|
||||
internal U64 str8_buffer_write_u32(String8Node *buf, U64 *pos, U32 v);
|
||||
internal U64 str8_buffer_write_zeroes(String8Node *buf, U64 *pos, U64 size);
|
||||
internal U64 str8_buffer_write_string_list(String8Node *buf, U64 *pos, String8List list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic String Hashes
|
||||
|
||||
internal U64 u64_hash_from_seed_str8(U64 seed, String8 string);
|
||||
internal U64 u64_hash_from_str8(String8 string);
|
||||
internal U128 u128_hash_from_seed_str8(U64 seed, String8 string);
|
||||
internal U128 u128_hash_from_str8(String8 string);
|
||||
|
||||
#endif // BASE_STRINGS_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_STRINGS_H
|
||||
#define BASE_STRINGS_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Third Party Includes
|
||||
|
||||
#define STB_SPRINTF_DECORATE(name) raddbg_##name
|
||||
#include "third_party/stb/stb_sprintf.h"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Types
|
||||
|
||||
typedef struct String8 String8;
|
||||
struct String8
|
||||
{
|
||||
U8 *str;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
typedef struct String16 String16;
|
||||
struct String16
|
||||
{
|
||||
U16 *str;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
typedef struct String32 String32;
|
||||
struct String32
|
||||
{
|
||||
U32 *str;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String List & Array Types
|
||||
|
||||
typedef struct String8Node String8Node;
|
||||
struct String8Node
|
||||
{
|
||||
String8Node *next;
|
||||
String8 string;
|
||||
};
|
||||
|
||||
typedef struct String8MetaNode String8MetaNode;
|
||||
struct String8MetaNode
|
||||
{
|
||||
String8MetaNode *next;
|
||||
String8Node *node;
|
||||
};
|
||||
|
||||
typedef struct String8List String8List;
|
||||
struct String8List
|
||||
{
|
||||
String8Node *first;
|
||||
String8Node *last;
|
||||
U64 node_count;
|
||||
U64 total_size;
|
||||
};
|
||||
|
||||
typedef struct String8Array String8Array;
|
||||
struct String8Array
|
||||
{
|
||||
String8 *strings;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Matching, Splitting, & Joining Types
|
||||
|
||||
typedef U32 StringMatchFlags;
|
||||
enum
|
||||
{
|
||||
StringMatchFlag_CaseInsensitive = (1 << 0),
|
||||
StringMatchFlag_RightSideSloppy = (1 << 1),
|
||||
StringMatchFlag_SlashInsensitive = (1 << 2),
|
||||
};
|
||||
|
||||
typedef U32 StringSplitFlags;
|
||||
enum
|
||||
{
|
||||
StringSplitFlag_KeepEmpties = (1 << 0),
|
||||
};
|
||||
|
||||
typedef enum PathStyle
|
||||
{
|
||||
PathStyle_Relative,
|
||||
PathStyle_WindowsAbsolute,
|
||||
PathStyle_UnixAbsolute,
|
||||
|
||||
#if OS_WINDOWS
|
||||
PathStyle_SystemAbsolute = PathStyle_WindowsAbsolute
|
||||
#elif OS_LINUX
|
||||
PathStyle_SystemAbsolute = PathStyle_UnixAbsolute
|
||||
#else
|
||||
# error "absolute path style is undefined for this OS"
|
||||
#endif
|
||||
}
|
||||
PathStyle;
|
||||
|
||||
typedef struct StringJoin StringJoin;
|
||||
struct StringJoin
|
||||
{
|
||||
String8 pre;
|
||||
String8 sep;
|
||||
String8 post;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Pair Types
|
||||
|
||||
typedef struct String8TxtPtPair String8TxtPtPair;
|
||||
struct String8TxtPtPair
|
||||
{
|
||||
String8 string;
|
||||
TxtPt pt;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: UTF Decoding Types
|
||||
|
||||
typedef struct UnicodeDecode UnicodeDecode;
|
||||
struct UnicodeDecode
|
||||
{
|
||||
U32 inc;
|
||||
U32 codepoint;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Fuzzy Matching Types
|
||||
|
||||
typedef struct FuzzyMatchRangeNode FuzzyMatchRangeNode;
|
||||
struct FuzzyMatchRangeNode
|
||||
{
|
||||
FuzzyMatchRangeNode *next;
|
||||
Rng1U64 range;
|
||||
};
|
||||
|
||||
typedef struct FuzzyMatchRangeList FuzzyMatchRangeList;
|
||||
struct FuzzyMatchRangeList
|
||||
{
|
||||
FuzzyMatchRangeNode *first;
|
||||
FuzzyMatchRangeNode *last;
|
||||
U64 count;
|
||||
U64 needle_part_count;
|
||||
U64 total_dim;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Character Classification & Conversion Functions
|
||||
|
||||
internal B32 char_is_space(U8 c);
|
||||
internal B32 char_is_upper(U8 c);
|
||||
internal B32 char_is_lower(U8 c);
|
||||
internal B32 char_is_alpha(U8 c);
|
||||
internal B32 char_is_slash(U8 c);
|
||||
internal B32 char_is_digit(U8 c, U32 base);
|
||||
internal U8 char_to_lower(U8 c);
|
||||
internal U8 char_to_upper(U8 c);
|
||||
internal U8 char_to_correct_slash(U8 c);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: C-String Measurement
|
||||
|
||||
internal U64 cstring8_length(U8 *c);
|
||||
internal U64 cstring16_length(U16 *c);
|
||||
internal U64 cstring32_length(U32 *c);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Constructors
|
||||
|
||||
#define str8_lit(S) str8((U8*)(S), sizeof(S) - 1)
|
||||
#define str8_lit_comp(S) {(U8*)(S), sizeof(S) - 1,}
|
||||
#define str8_varg(S) (int)((S).size), ((S).str)
|
||||
|
||||
#define str8_array(S,C) str8((U8*)(S), sizeof(*(S))*(C))
|
||||
#define str8_array_fixed(S) str8((U8*)(S), sizeof(S))
|
||||
#define str8_struct(S) str8((U8*)(S), sizeof(*(S)))
|
||||
|
||||
internal String8 str8(U8 *str, U64 size);
|
||||
internal String8 str8_range(U8 *first, U8 *one_past_last);
|
||||
internal String8 str8_zero(void);
|
||||
internal String16 str16(U16 *str, U64 size);
|
||||
internal String16 str16_range(U16 *first, U16 *one_past_last);
|
||||
internal String16 str16_zero(void);
|
||||
internal String32 str32(U32 *str, U64 size);
|
||||
internal String32 str32_range(U32 *first, U32 *one_past_last);
|
||||
internal String32 str32_zero(void);
|
||||
internal String8 str8_cstring(char *c);
|
||||
internal String16 str16_cstring(U16 *c);
|
||||
internal String32 str32_cstring(U32 *c);
|
||||
internal String8 str8_cstring_capped(void *cstr, void *cap);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Stylization
|
||||
|
||||
internal String8 upper_from_str8(Arena *arena, String8 string);
|
||||
internal String8 lower_from_str8(Arena *arena, String8 string);
|
||||
internal String8 backslashed_from_str8(Arena *arena, String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Matching
|
||||
|
||||
internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags);
|
||||
internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
|
||||
internal B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Slicing
|
||||
|
||||
internal String8 str8_substr(String8 str, Rng1U64 range);
|
||||
internal String8 str8_prefix(String8 str, U64 size);
|
||||
internal String8 str8_skip(String8 str, U64 amt);
|
||||
internal String8 str8_postfix(String8 str, U64 size);
|
||||
internal String8 str8_chop(String8 str, U64 amt);
|
||||
internal String8 str8_skip_chop_whitespace(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Formatting & Copying
|
||||
|
||||
internal String8 push_str8_cat(Arena *arena, String8 s1, String8 s2);
|
||||
internal String8 push_str8_copy(Arena *arena, String8 s);
|
||||
internal String8 push_str8fv(Arena *arena, char *fmt, va_list args);
|
||||
internal String8 push_str8f(Arena *arena, char *fmt, ...);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <=> Integer Conversions
|
||||
|
||||
//- rjf: string -> integer
|
||||
internal S64 sign_from_str8(String8 string, String8 *string_tail);
|
||||
internal B32 str8_is_integer(String8 string, U32 radix);
|
||||
internal U64 u64_from_str8(String8 string, U32 radix);
|
||||
internal S64 s64_from_str8(String8 string, U32 radix);
|
||||
internal B32 try_u64_from_str8_c_rules(String8 string, U64 *x);
|
||||
internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x);
|
||||
|
||||
//- rjf: integer -> string
|
||||
internal String8 str8_from_memory_size(Arena *arena, U64 z);
|
||||
internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator);
|
||||
internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <=> Float Conversions
|
||||
|
||||
internal F64 f64_from_str8(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String List Construction Functions
|
||||
|
||||
internal String8Node* str8_list_push_node(String8List *list, String8Node *node);
|
||||
internal String8Node* str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string);
|
||||
internal String8Node* str8_list_push_node_front(String8List *list, String8Node *node);
|
||||
internal String8Node* str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string);
|
||||
internal String8Node* str8_list_push(Arena *arena, String8List *list, String8 string);
|
||||
internal String8Node* str8_list_push_front(Arena *arena, String8List *list, String8 string);
|
||||
internal void str8_list_concat_in_place(String8List *list, String8List *to_push);
|
||||
internal String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align);
|
||||
internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...);
|
||||
internal String8Node* str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...);
|
||||
internal String8List str8_list_copy(Arena *arena, String8List *list);
|
||||
#define str8_list_first(list) ((list)->first ? (list)->first->string : str8_zero())
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Splitting & Joining
|
||||
|
||||
internal String8List str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags);
|
||||
internal String8List str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags);
|
||||
internal String8List str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags);
|
||||
internal String8 str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params);
|
||||
internal void str8_list_from_flags(Arena *arena, String8List *list, U32 flags, String8 *flag_string_table, U32 flag_string_count);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf; String Arrays
|
||||
|
||||
internal String8Array str8_array_from_list(Arena *arena, String8List *list);
|
||||
internal String8Array str8_array_reserve(Arena *arena, U64 count);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Path Helpers
|
||||
|
||||
internal String8 str8_chop_last_slash(String8 string);
|
||||
internal String8 str8_skip_last_slash(String8 string);
|
||||
internal String8 str8_chop_last_dot(String8 string);
|
||||
internal String8 str8_skip_last_dot(String8 string);
|
||||
|
||||
internal PathStyle path_style_from_str8(String8 string);
|
||||
internal String8List str8_split_path(Arena *arena, String8 string);
|
||||
internal void str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style);
|
||||
internal String8 str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style);
|
||||
|
||||
internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: UTF-8 & UTF-16 Decoding/Encoding
|
||||
|
||||
internal UnicodeDecode utf8_decode(U8 *str, U64 max);
|
||||
internal UnicodeDecode utf16_decode(U16 *str, U64 max);
|
||||
internal U32 utf8_encode(U8 *str, U32 codepoint);
|
||||
internal U32 utf16_encode(U16 *str, U32 codepoint);
|
||||
internal U32 utf8_from_utf32_single(U8 *buffer, U32 character);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Unicode String Conversions
|
||||
|
||||
internal String8 str8_from_16(Arena *arena, String16 in);
|
||||
internal String16 str16_from_8(Arena *arena, String8 in);
|
||||
internal String8 str8_from_32(Arena *arena, String32 in);
|
||||
internal String32 str32_from_8(Arena *arena, String8 in);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Types & Space Enum -> String Conversions
|
||||
|
||||
internal String8 string_from_dimension(Dimension dimension);
|
||||
internal String8 string_from_side(Side side);
|
||||
internal String8 string_from_operating_system(OperatingSystem os);
|
||||
internal String8 string_from_architecture(Architecture arch);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Time Types -> String
|
||||
|
||||
internal String8 string_from_week_day(WeekDay week_day);
|
||||
internal String8 string_from_month(Month month);
|
||||
internal String8 push_date_time_string(Arena *arena, DateTime *date_time);
|
||||
internal String8 push_file_name_date_time_string(Arena *arena, DateTime *date_time);
|
||||
internal String8 string_from_elapsed_time(Arena *arena, DateTime dt);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Textual String Wrapping
|
||||
|
||||
internal String8List wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <-> Color
|
||||
|
||||
internal String8 hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba);
|
||||
internal Vec4F32 rgba_from_hex_string_4f32(String8 hex_string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Fuzzy Matching
|
||||
|
||||
internal FuzzyMatchRangeList fuzzy_match_find(Arena *arena, String8 needle, String8 haystack);
|
||||
internal FuzzyMatchRangeList fuzzy_match_range_list_copy(Arena *arena, FuzzyMatchRangeList *src);
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(allen): Serialization Helpers
|
||||
|
||||
internal void str8_serial_begin(Arena *arena, String8List *srl);
|
||||
internal String8 str8_serial_end(Arena *arena, String8List *srl);
|
||||
internal void str8_serial_write_to_dst(String8List *srl, void *out);
|
||||
internal U64 str8_serial_push_align(Arena *arena, String8List *srl, U64 align);
|
||||
internal void * str8_serial_push_size(Arena *arena, String8List *srl, U64 size);
|
||||
internal void * str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size);
|
||||
internal void str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first);
|
||||
internal void str8_serial_push_u64(Arena *arena, String8List *srl, U64 x);
|
||||
internal void str8_serial_push_u32(Arena *arena, String8List *srl, U32 x);
|
||||
internal void str8_serial_push_u16(Arena *arena, String8List *srl, U16 x);
|
||||
internal void str8_serial_push_u8(Arena *arena, String8List *srl, U8 x);
|
||||
internal void str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str);
|
||||
internal void str8_serial_push_string(Arena *arena, String8List *srl, String8 str);
|
||||
#define str8_serial_push_array(arena, srl, ptr, count) str8_serial_push_data(arena, srl, ptr, sizeof(*(ptr)) * (count))
|
||||
#define str8_serial_push_struct(arena, srl, ptr) str8_serial_push_array(arena, srl, ptr, 1)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Deserialization Helpers
|
||||
|
||||
internal U64 str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity);
|
||||
internal U64 str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val);
|
||||
internal void * str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size);internal U64 str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out);
|
||||
internal U64 str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out);
|
||||
internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out);
|
||||
#define str8_deserial_read_array(string, off, ptr, count) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr))*(count), sizeof(*(ptr)))
|
||||
#define str8_deserial_read_struct(string, off, ptr) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr)), sizeof(*(ptr)))
|
||||
|
||||
#endif // BASE_STRINGS_H
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_SYSTEM_H
|
||||
#define BASE_SYSTEM_H
|
||||
|
||||
typedef struct SystemInfo SystemInfo;
|
||||
struct SystemInfo
|
||||
{
|
||||
U32 logical_processor_count;
|
||||
U64 page_size;
|
||||
U64 large_page_size;
|
||||
U64 allocation_granularity;
|
||||
String8 machine_name;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl System Info
|
||||
|
||||
internal SystemInfo *get_system_info(void);
|
||||
|
||||
#endif // BASE_SYSTEM_H
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#if BUILD_TESTS
|
||||
|
||||
internal String8
|
||||
test_build_exe_path(Arena *arena, String8 name)
|
||||
{
|
||||
String8 folder = get_process_info()->binary_path;
|
||||
String8 path = str8f(arena, "%S/%S%S", folder, name, program_ext_postfix_from_os(OperatingSystem_CURRENT, 0));
|
||||
return path;
|
||||
}
|
||||
|
||||
internal String8
|
||||
test_input_path(Arena *arena, TestCtx *ctx, String8 name)
|
||||
{
|
||||
String8 path = str8f(arena, "%S/%S", ctx->input_data_path, name);
|
||||
return path;
|
||||
}
|
||||
|
||||
internal String8
|
||||
test_input_exe_path(Arena *arena, TestCtx *ctx, String8 name)
|
||||
{
|
||||
String8 path = str8f(arena, "%S/%S%S", ctx->input_data_path, name, program_ext_postfix_from_os(OperatingSystem_CURRENT, 1));
|
||||
return path;
|
||||
}
|
||||
|
||||
internal String8
|
||||
test_exemplar_path(Arena *arena, TestCtx *ctx, String8 name)
|
||||
{
|
||||
String8 path = str8f(arena, "%S/%S", ctx->exemplars_path, name);
|
||||
return path;
|
||||
}
|
||||
|
||||
internal void
|
||||
base_register_test(char *func_name_cstr, TestFunctionType *fn, char *file_path_cstr, int line, int skip)
|
||||
{
|
||||
String8 file_path = str8_cstring(file_path_cstr);
|
||||
U64 src_min = str8_find_needle(file_path, 0, s("src/"), StringMatchFlag_SlashInsensitive);
|
||||
U64 src_max = src_min + str8_lit("src/").size;
|
||||
U64 layer_slash_max = str8_find_needle(file_path, src_max, s("/"), StringMatchFlag_SlashInsensitive);
|
||||
|
||||
TestInfo *t = &test_infos[test_infos_count++];
|
||||
t->layer = str8_substr(file_path, r1u64(src_max, layer_slash_max));
|
||||
t->label = str8_cstring(func_name_cstr);
|
||||
t->decl_line = line;
|
||||
t->skip = skip;
|
||||
t->test_fn = fn;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_TEST_H
|
||||
#define BASE_TEST_H
|
||||
|
||||
typedef enum TestStatus
|
||||
{
|
||||
TestStatus_Fail,
|
||||
TestStatus_Crash,
|
||||
TestStatus_Pass,
|
||||
TestStatus_Skip,
|
||||
TestStatus_COUNT
|
||||
}
|
||||
TestStatus;
|
||||
|
||||
typedef struct TestResult TestResult;
|
||||
struct TestResult
|
||||
{
|
||||
TestStatus status;
|
||||
char *fail_file;
|
||||
int fail_line;
|
||||
char *fail_cond;
|
||||
};
|
||||
|
||||
typedef struct TestCtx TestCtx;
|
||||
struct TestCtx
|
||||
{
|
||||
CmdLine *cmdline;
|
||||
String8 exemplars_path;
|
||||
String8 artifacts_path;
|
||||
String8 input_data_path;
|
||||
TestResult *result_out;
|
||||
String8List *test_out;
|
||||
};
|
||||
|
||||
#define TEST_FUNCTION_SIG(name) void name(Arena *arena, TestCtx *ctx)
|
||||
#define TEST_FUNCTION_DEF(name) TEST_FUNCTION_SIG(test__##name)
|
||||
typedef TEST_FUNCTION_SIG(TestFunctionType);
|
||||
|
||||
typedef struct TestInfo TestInfo;
|
||||
struct TestInfo
|
||||
{
|
||||
String8 layer;
|
||||
String8 label;
|
||||
S64 decl_line;
|
||||
B32 skip;
|
||||
TestFunctionType *test_fn;
|
||||
};
|
||||
|
||||
#if BUILD_TESTS
|
||||
|
||||
// alloc storage for the tests
|
||||
global U16 test_infos_count = 0;
|
||||
global TestInfo test_infos[0xffff] = {0};
|
||||
|
||||
internal String8 test_build_exe_path(Arena *arena, String8 name);
|
||||
internal String8 test_input_path(Arena *arena, TestCtx *ctx, String8 name);
|
||||
internal String8 test_input_exe_path(Arena *arena, TestCtx *ctx, String8 name);
|
||||
internal String8 test_exemplar_path(Arena *arena, TestCtx *ctx, String8 name);
|
||||
internal void base_register_test(char *func_name, TestFunctionType *fn, char *file_path, int line, int skip);
|
||||
|
||||
#define AddTest(name, file_path, line, skip_, ...) \
|
||||
TEST_FUNCTION_DEF(name); \
|
||||
__VA_ARGS__ void add_test__##name(void) { base_register_test(Stringify(name), test__##name, file_path, line, skip_); }
|
||||
|
||||
# if COMPILER_MSVC
|
||||
# pragma section(".CRT$XCU", read)
|
||||
# define DeclareTest(name, skip) \
|
||||
/* register test */ AddTest(name, __FILE__, __LINE__, (skip)) \
|
||||
/* alloc function pointer */ __declspec(allocate(".CRT$XCU")) void (*add_test_ptr__##name)(void) = add_test__##name; \
|
||||
/* do not GC test caller */ __pragma(comment(linker, "/include:" Stringify(add_test_ptr__##name)))
|
||||
# elif COMPILER_GCC || COMPILER_CLANG
|
||||
// clang and gcc allocate memory for the function pointer automatically
|
||||
# define DeclareTest(name, skip) AddTest(name, __FILE__, __LINE__, (skip), __attribute__((constructor)))
|
||||
# else
|
||||
# error DeclareTest not defined for this compiler.
|
||||
# endif
|
||||
#else
|
||||
# define DeclareTest(name, skip)
|
||||
#endif
|
||||
|
||||
#define Test(name) DeclareTest(name, 0) TEST_FUNCTION_DEF(name)
|
||||
#define SkippedTest(name) DeclareTest(name, 1) TEST_FUNCTION_DEF(name)
|
||||
|
||||
#define TestCheck(c) do { if (!(c)) { \
|
||||
/* record failed check */ ctx->result_out[0] = (TestResult){ .fail_file = __FILE__, .fail_line = __LINE__, .fail_cond = Stringify(c) }; \
|
||||
/* under debugger? -> trap */ if(debugger_is_attached()) { Trap(); } \
|
||||
/* exit test */ return; \
|
||||
} } while(0)
|
||||
|
||||
#define TestSkip() do { \
|
||||
ctx->result_out[0] = (TestResult){ .status = TestStatus_Skip }; \
|
||||
return; \
|
||||
} while(0)
|
||||
|
||||
// test log
|
||||
#define test_out(string) str8_list_push(arena, ctx->test_out, (string))
|
||||
#define test_outf(...) str8_list_pushf(arena, ctx->test_out, __VA_ARGS__)
|
||||
|
||||
#endif // BASE_TEST_H
|
||||
+87
-234
@@ -1,234 +1,87 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
C_LINKAGE thread_static TCTX *tctx_thread_local;
|
||||
#if !BUILD_SUPPLEMENTARY_UNIT
|
||||
C_LINKAGE thread_static TCTX *tctx_thread_local = 0;
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Context Functions
|
||||
|
||||
//- rjf: thread-context allocation & selection
|
||||
|
||||
internal TCTX *
|
||||
tctx_alloc(void)
|
||||
{
|
||||
#if PROFILE_TELEMETRY
|
||||
thread_static static char name[2][1024];
|
||||
raddbg_snprintf(name[0], sizeof(name[0]), "Scratch/0[TID:%u]", tid());
|
||||
raddbg_snprintf(name[1], sizeof(name[1]), "Scratch/1[TID:%u]", tid());
|
||||
Arena *arena_0 = arena_alloc(.name = name[0]);
|
||||
Arena *arena_1 = arena_alloc(.name = name[1]);
|
||||
#else
|
||||
Arena *arena_0 = arena_alloc();
|
||||
Arena *arena_1 = arena_alloc();
|
||||
#endif
|
||||
TCTX *tctx = push_array(arena_0, TCTX, 1);
|
||||
tctx->arenas[0] = arena_0;
|
||||
tctx->arenas[1] = arena_1;
|
||||
tctx->lane_ctx.lane_count = 1;
|
||||
return tctx;
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_release(TCTX *tctx)
|
||||
{
|
||||
arena_release(tctx->arenas[1]);
|
||||
arena_release(tctx->arenas[0]);
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_select(TCTX *tctx)
|
||||
{
|
||||
tctx_thread_local = tctx;
|
||||
}
|
||||
|
||||
internal TCTX *
|
||||
tctx_selected(void)
|
||||
{
|
||||
return tctx_thread_local;
|
||||
}
|
||||
|
||||
//- rjf: scratch arenas
|
||||
|
||||
internal Arena *
|
||||
tctx_get_scratch(Arena **conflicts, U64 count)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
Arena *result = 0;
|
||||
Arena **arena_ptr = tctx->arenas;
|
||||
for(U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1)
|
||||
{
|
||||
Arena **conflict_ptr = conflicts;
|
||||
B32 has_conflict = 0;
|
||||
for(U64 j = 0; j < count; j += 1, conflict_ptr += 1)
|
||||
{
|
||||
if(*arena_ptr == *conflict_ptr)
|
||||
{
|
||||
has_conflict = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!has_conflict)
|
||||
{
|
||||
result = *arena_ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//- rjf: lane metadata
|
||||
|
||||
internal LaneCtx
|
||||
tctx_set_lane_ctx(LaneCtx lane_ctx)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
LaneCtx restore = tctx->lane_ctx;
|
||||
tctx->lane_ctx = lane_ctx;
|
||||
return restore;
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_lane_barrier_wait(void *broadcast_ptr, U64 broadcast_size, U64 broadcast_src_lane_idx)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
ProfColor(0x00000ff);
|
||||
TCTX *tctx = tctx_selected();
|
||||
|
||||
// rjf: doing broadcast -> copy to broadcast memory on source lane
|
||||
U64 broadcast_size_clamped = ClampTop(broadcast_size, sizeof(tctx->lane_ctx.broadcast_memory[0]));
|
||||
if(broadcast_ptr != 0 && lane_idx() == broadcast_src_lane_idx)
|
||||
{
|
||||
MemoryCopy(tctx->lane_ctx.broadcast_memory, broadcast_ptr, broadcast_size_clamped);
|
||||
}
|
||||
|
||||
// rjf: all cases: barrier
|
||||
barrier_wait(tctx->lane_ctx.barrier);
|
||||
|
||||
// rjf: doing broadcast -> copy from broadcast memory on destination lanes
|
||||
if(broadcast_ptr != 0 && lane_idx() != broadcast_src_lane_idx)
|
||||
{
|
||||
MemoryCopy(broadcast_ptr, tctx->lane_ctx.broadcast_memory, broadcast_size_clamped);
|
||||
}
|
||||
|
||||
// rjf: doing broadcast -> barrier on all lanes
|
||||
if(broadcast_ptr != 0)
|
||||
{
|
||||
barrier_wait(tctx->lane_ctx.barrier);
|
||||
}
|
||||
|
||||
ProfEnd();
|
||||
}
|
||||
|
||||
//- rjf: thread names
|
||||
|
||||
internal void
|
||||
tctx_set_thread_name(String8 string)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
U64 size = ClampTop(string.size, sizeof(tctx->thread_name));
|
||||
MemoryCopy(tctx->thread_name, string.str, size);
|
||||
tctx->thread_name_size = size;
|
||||
}
|
||||
|
||||
internal String8
|
||||
tctx_get_thread_name(void)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
String8 result = str8(tctx->thread_name, tctx->thread_name_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
//- rjf: thread source-locations
|
||||
|
||||
internal void
|
||||
tctx_write_srcloc(char *file_name, U64 line_number)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
tctx->file_name = file_name;
|
||||
tctx->line_number = line_number;
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_read_srcloc(char **file_name, U64 *line_number)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
*file_name = tctx->file_name;
|
||||
*line_number = tctx->line_number;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Touch Scope Functions
|
||||
|
||||
internal Access *
|
||||
access_open(void)
|
||||
{
|
||||
if(tctx_thread_local->access_arena == 0)
|
||||
{
|
||||
tctx_thread_local->access_arena = arena_alloc();
|
||||
}
|
||||
Access *access = tctx_thread_local->free_access;
|
||||
if(access != 0)
|
||||
{
|
||||
SLLStackPop(tctx_thread_local->free_access);
|
||||
}
|
||||
else
|
||||
{
|
||||
access = push_array_no_zero(tctx_thread_local->access_arena, Access, 1);
|
||||
}
|
||||
MemoryZeroStruct(access);
|
||||
return access;
|
||||
}
|
||||
|
||||
internal void
|
||||
access_close(Access *access)
|
||||
{
|
||||
for(Touch *touch = access->top_touch, *next = 0; touch != 0; touch = next)
|
||||
{
|
||||
next = touch->next;
|
||||
ins_atomic_u64_dec_eval(&touch->pt->access_refcount);
|
||||
if(touch->cv.u64[0] != 0) { cond_var_broadcast(touch->cv); }
|
||||
SLLStackPush(tctx_thread_local->free_touch, touch);
|
||||
}
|
||||
SLLStackPush(tctx_thread_local->free_access, access);
|
||||
}
|
||||
|
||||
internal void
|
||||
access_touch(Access *access, AccessPt *pt, CondVar cv)
|
||||
{
|
||||
ins_atomic_u64_inc_eval(&pt->access_refcount);
|
||||
ins_atomic_u64_eval_assign(&pt->last_time_touched_us, now_time_us());
|
||||
ins_atomic_u64_eval_assign(&pt->last_update_idx_touched, update_tick_idx());
|
||||
Touch *touch = tctx_thread_local->free_touch;
|
||||
if(touch != 0)
|
||||
{
|
||||
SLLStackPop(tctx_thread_local->free_touch);
|
||||
}
|
||||
else
|
||||
{
|
||||
touch = push_array_no_zero(tctx_thread_local->access_arena, Touch, 1);
|
||||
}
|
||||
MemoryZeroStruct(touch);
|
||||
SLLStackPush(access->top_touch, touch);
|
||||
touch->cv = cv;
|
||||
touch->pt = pt;
|
||||
}
|
||||
|
||||
//- rjf: access points
|
||||
|
||||
internal B32
|
||||
access_pt_is_expired_(AccessPt *pt, AccessPtExpireParams *params)
|
||||
{
|
||||
U64 access_refcount = ins_atomic_u64_eval(&pt->access_refcount);
|
||||
U64 last_time_touched_us = ins_atomic_u64_eval(&pt->last_time_touched_us);
|
||||
U64 last_update_idx_touched = ins_atomic_u64_eval(&pt->last_update_idx_touched);
|
||||
B32 result = (access_refcount == 0 &&
|
||||
last_time_touched_us + params->time <= now_time_us() &&
|
||||
last_update_idx_touched + params->update_idxs <= update_tick_idx());
|
||||
return result;
|
||||
}
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
// NOTE(allen): Thread Context Functions
|
||||
|
||||
C_LINKAGE thread_static TCTX* tctx_thread_local;
|
||||
#if !BUILD_SUPPLEMENTARY_UNIT
|
||||
C_LINKAGE thread_static TCTX* tctx_thread_local = 0;
|
||||
#endif
|
||||
|
||||
internal void
|
||||
tctx_init_and_equip(TCTX *tctx){
|
||||
MemoryZeroStruct(tctx);
|
||||
Arena **arena_ptr = tctx->arenas;
|
||||
for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){
|
||||
*arena_ptr = arena_alloc();
|
||||
}
|
||||
tctx_thread_local = tctx;
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_release(void)
|
||||
{
|
||||
for(U64 i = 0; i < ArrayCount(tctx_thread_local->arenas); i += 1)
|
||||
{
|
||||
arena_release(tctx_thread_local->arenas[i]);
|
||||
}
|
||||
}
|
||||
|
||||
internal TCTX*
|
||||
tctx_get_equipped(void){
|
||||
return(tctx_thread_local);
|
||||
}
|
||||
|
||||
internal Arena*
|
||||
tctx_get_scratch(Arena **conflicts, U64 count){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
|
||||
Arena *result = 0;
|
||||
Arena **arena_ptr = tctx->arenas;
|
||||
for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){
|
||||
Arena **conflict_ptr = conflicts;
|
||||
B32 has_conflict = 0;
|
||||
for (U64 j = 0; j < count; j += 1, conflict_ptr += 1){
|
||||
if (*arena_ptr == *conflict_ptr){
|
||||
has_conflict = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_conflict){
|
||||
result = *arena_ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_set_thread_name(String8 string){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
U64 size = ClampTop(string.size, sizeof(tctx->thread_name));
|
||||
MemoryCopy(tctx->thread_name, string.str, size);
|
||||
tctx->thread_name_size = size;
|
||||
}
|
||||
|
||||
internal String8
|
||||
tctx_get_thread_name(void){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
String8 result = str8(tctx->thread_name, tctx->thread_name_size);
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_write_srcloc(char *file_name, U64 line_number){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
tctx->file_name = file_name;
|
||||
tctx->line_number = line_number;
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_read_srcloc(char **file_name, U64 *line_number){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
*file_name = tctx->file_name;
|
||||
*line_number = tctx->line_number;
|
||||
}
|
||||
|
||||
+41
-132
@@ -1,132 +1,41 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_THREAD_CONTEXT_H
|
||||
#define BASE_THREAD_CONTEXT_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Lane Context
|
||||
|
||||
typedef struct LaneCtx LaneCtx;
|
||||
struct LaneCtx
|
||||
{
|
||||
U64 lane_idx;
|
||||
U64 lane_count;
|
||||
Barrier barrier;
|
||||
U64 *broadcast_memory;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Access Scopes
|
||||
|
||||
typedef struct AccessPt AccessPt;
|
||||
struct AccessPt
|
||||
{
|
||||
U64 access_refcount;
|
||||
U64 last_time_touched_us;
|
||||
U64 last_update_idx_touched;
|
||||
};
|
||||
|
||||
typedef struct AccessPtExpireParams AccessPtExpireParams;
|
||||
struct AccessPtExpireParams
|
||||
{
|
||||
U64 time;
|
||||
U64 update_idxs;
|
||||
};
|
||||
|
||||
typedef struct Touch Touch;
|
||||
struct Touch
|
||||
{
|
||||
Touch *next;
|
||||
AccessPt *pt;
|
||||
CondVar cv;
|
||||
};
|
||||
|
||||
typedef struct Access Access;
|
||||
struct Access
|
||||
{
|
||||
Access *next;
|
||||
Touch *top_touch;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Base Per-Thread State Bundle
|
||||
|
||||
typedef struct TCTX TCTX;
|
||||
struct TCTX
|
||||
{
|
||||
// rjf: scratch arenas
|
||||
Arena *arenas[2];
|
||||
|
||||
// rjf: thread name
|
||||
U8 thread_name[32];
|
||||
U64 thread_name_size;
|
||||
|
||||
// rjf: lane context
|
||||
LaneCtx lane_ctx;
|
||||
|
||||
// rjf: source location info
|
||||
char *file_name;
|
||||
U64 line_number;
|
||||
|
||||
// rjf: accesses
|
||||
Arena *access_arena;
|
||||
Access *free_access;
|
||||
Touch *free_touch;
|
||||
|
||||
// rjf: progress
|
||||
U64 *progress_counter_ptr;
|
||||
U64 *progress_target_ptr;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Context Functions
|
||||
|
||||
//- rjf: thread-context allocation & selection
|
||||
internal TCTX *tctx_alloc(void);
|
||||
internal void tctx_release(TCTX *tctx);
|
||||
internal void tctx_select(TCTX *tctx);
|
||||
internal TCTX *tctx_selected(void);
|
||||
|
||||
//- rjf: scratch arenas
|
||||
internal Arena *tctx_get_scratch(Arena **conflicts, U64 count);
|
||||
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
|
||||
#define scratch_end(scratch) temp_end(scratch)
|
||||
|
||||
//- rjf: lane metadata
|
||||
internal LaneCtx tctx_set_lane_ctx(LaneCtx lane_ctx);
|
||||
internal void tctx_lane_barrier_wait(void *broadcast_ptr, U64 broadcast_size, U64 broadcast_src_lane_idx);
|
||||
#define lane_idx() (tctx_selected()->lane_ctx.lane_idx)
|
||||
#define lane_count() (tctx_selected()->lane_ctx.lane_count)
|
||||
#define lane_from_task_idx(idx) ((idx)%lane_count())
|
||||
#define lane_ctx(ctx) tctx_set_lane_ctx((ctx))
|
||||
#define lane_sync() tctx_lane_barrier_wait(0, 0, 0)
|
||||
#define lane_sync_u64(ptr, src_lane_idx) tctx_lane_barrier_wait((ptr), sizeof(*(ptr)), (src_lane_idx))
|
||||
#define lane_range(count) m_range_from_n_idx_m_count(lane_idx(), lane_count(), (count))
|
||||
|
||||
//- rjf: thread names
|
||||
internal void tctx_set_thread_name(String8 name);
|
||||
internal String8 tctx_get_thread_name(void);
|
||||
|
||||
//- rjf: thread source-locations
|
||||
internal void tctx_write_srcloc(char *file_name, U64 line_number);
|
||||
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
|
||||
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
|
||||
|
||||
//- rjf: access scopes
|
||||
internal Access *access_open(void);
|
||||
internal void access_close(Access *access);
|
||||
internal void access_touch(Access *access, AccessPt *pt, CondVar cv);
|
||||
|
||||
//- rjf: access points
|
||||
internal B32 access_pt_is_expired_(AccessPt *pt, AccessPtExpireParams *params);
|
||||
#define access_pt_is_expired(pt, ...) access_pt_is_expired_((pt), &(AccessPtExpireParams){.time = 2000000, .update_idxs = 2, __VA_ARGS__})
|
||||
|
||||
//- rjf: progress counters
|
||||
#define set_progress_ptr(ptr) (tctx_selected()->progress_counter_ptr = (ptr))
|
||||
#define set_progress_target_ptr(ptr) (tctx_selected()->progress_target_ptr = (ptr))
|
||||
#define set_progress(val) (tctx_selected()->progress_counter_ptr ? ins_atomic_u64_eval_assign(tctx_selected()->progress_counter_ptr, (val)) : (void)0)
|
||||
#define add_progress(val) (tctx_selected()->progress_counter_ptr ? ins_atomic_u64_add_eval(tctx_selected()->progress_counter_ptr, (val)) : (void)0)
|
||||
#define set_progress_target(val) (tctx_selected()->progress_target_ptr ? ins_atomic_u64_eval_assign(tctx_selected()->progress_target_ptr, (val)) : (void)0)
|
||||
|
||||
#endif // BASE_THREAD_CONTEXT_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_THREAD_CONTEXT_H
|
||||
#define BASE_THREAD_CONTEXT_H
|
||||
|
||||
////////////////////////////////
|
||||
// NOTE(allen): Thread Context
|
||||
|
||||
typedef struct TCTX TCTX;
|
||||
struct TCTX
|
||||
{
|
||||
Arena *arenas[2];
|
||||
|
||||
U8 thread_name[32];
|
||||
U64 thread_name_size;
|
||||
|
||||
char *file_name;
|
||||
U64 line_number;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// NOTE(allen): Thread Context Functions
|
||||
|
||||
internal void tctx_init_and_equip(TCTX *tctx);
|
||||
internal void tctx_release(void);
|
||||
internal TCTX* tctx_get_equipped(void);
|
||||
|
||||
internal Arena* tctx_get_scratch(Arena **conflicts, U64 count);
|
||||
|
||||
internal void tctx_set_thread_name(String8 name);
|
||||
internal String8 tctx_get_thread_name(void);
|
||||
|
||||
internal void tctx_write_srcloc(char *file_name, U64 line_number);
|
||||
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
|
||||
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
|
||||
|
||||
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
|
||||
#define scratch_end(scratch) temp_end(scratch)
|
||||
|
||||
#endif //BASE_THREAD_CONTEXT_H
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Table Stripe Functions
|
||||
|
||||
internal StripeArray
|
||||
stripe_array_alloc(Arena *arena)
|
||||
{
|
||||
StripeArray array = {0};
|
||||
array.count = get_system_info()->logical_processor_count;
|
||||
array.v = push_array(arena, Stripe, array.count);
|
||||
for EachIndex(idx, array.count)
|
||||
{
|
||||
array.v[idx].arena = arena_alloc();
|
||||
array.v[idx].rw_mutex = rw_mutex_alloc();
|
||||
array.v[idx].cv = cond_var_alloc();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
internal void
|
||||
stripe_array_release(StripeArray *stripes)
|
||||
{
|
||||
for EachIndex(idx, stripes->count)
|
||||
{
|
||||
arena_release(stripes->v[idx].arena);
|
||||
rw_mutex_release(stripes->v[idx].rw_mutex);
|
||||
cond_var_release(stripes->v[idx].cv);
|
||||
}
|
||||
}
|
||||
|
||||
internal Stripe *
|
||||
stripe_from_slot_idx(StripeArray *stripes, U64 slot_idx)
|
||||
{
|
||||
Stripe *stripe = &stripes->v[slot_idx%stripes->count];
|
||||
return stripe;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Info Helpers
|
||||
|
||||
internal void
|
||||
set_thread_name(String8 string)
|
||||
{
|
||||
ProfThreadName("%.*s", str8_varg(string));
|
||||
set_platform_thread_name(string);
|
||||
}
|
||||
|
||||
internal void
|
||||
set_thread_namef(char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
set_thread_name(string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Platform-Abstracted Synchronization Primitive Functions
|
||||
|
||||
//- rjf: slow barriers
|
||||
|
||||
typedef struct BarrierNode BarrierNode;
|
||||
struct BarrierNode
|
||||
{
|
||||
BarrierNode *next;
|
||||
U64 count;
|
||||
U64 threads_left_to_enter;
|
||||
U64 threads_left_to_leave;
|
||||
RWMutex rw_mutex;
|
||||
CondVar cv;
|
||||
};
|
||||
|
||||
typedef struct BarrierTCTX BarrierTCTX;
|
||||
struct BarrierTCTX
|
||||
{
|
||||
Arena *arena;
|
||||
BarrierNode *free_barrier_node;
|
||||
};
|
||||
|
||||
thread_static BarrierTCTX *barrier_tctx = 0;
|
||||
|
||||
internal Barrier
|
||||
slow_barrier_alloc(U64 count)
|
||||
{
|
||||
if(barrier_tctx == 0)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
barrier_tctx = push_array(arena, BarrierTCTX, 1);
|
||||
barrier_tctx->arena = arena;
|
||||
}
|
||||
BarrierNode *n = barrier_tctx->free_barrier_node;
|
||||
if(n != 0)
|
||||
{
|
||||
SLLStackPop(barrier_tctx->free_barrier_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = push_array_no_zero(barrier_tctx->arena, BarrierNode, 1);
|
||||
}
|
||||
MemoryZeroStruct(n);
|
||||
n->count = count;
|
||||
n->threads_left_to_enter = count;
|
||||
n->rw_mutex = rw_mutex_alloc();
|
||||
n->cv = cond_var_alloc();
|
||||
Barrier result = {(U64)n};
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
slow_barrier_release(Barrier barrier)
|
||||
{
|
||||
if(barrier_tctx == 0)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
barrier_tctx = push_array(arena, BarrierTCTX, 1);
|
||||
barrier_tctx->arena = arena;
|
||||
}
|
||||
BarrierNode *n = (BarrierNode *)barrier.u64[0];
|
||||
rw_mutex_release(n->rw_mutex);
|
||||
cond_var_release(n->cv);
|
||||
SLLStackPush(barrier_tctx->free_barrier_node, n);
|
||||
}
|
||||
|
||||
internal void
|
||||
slow_barrier_wait(Barrier barrier)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
BarrierNode *n = (BarrierNode *)barrier.u64[0];
|
||||
U64 threads_left_to_enter = ins_atomic_u64_dec_eval(&n->threads_left_to_enter);
|
||||
|
||||
//- rjf: threads left to enter > 0 => wait
|
||||
if(threads_left_to_enter > 0)
|
||||
{
|
||||
// rjf: first try a spin loop
|
||||
B32 done_waiting = 0;
|
||||
ProfScope("spin loop wait") for(U64 spin_count = 0; spin_count < 10000; spin_count += 1)
|
||||
{
|
||||
if(ins_atomic_u64_eval(&n->threads_left_to_leave) != 0)
|
||||
{
|
||||
done_waiting = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: not done waiting -> need to do slow wait on condition variable
|
||||
if(!done_waiting) ProfScope("slow wait")
|
||||
{
|
||||
RWMutexScope(n->rw_mutex, 0) for(;;)
|
||||
{
|
||||
if(ins_atomic_u64_eval(&n->threads_left_to_leave) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
cond_var_wait_rw(n->cv, n->rw_mutex, 0, max_U64);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: decrement leave counter
|
||||
if(ins_atomic_u64_dec_eval(&n->threads_left_to_leave) > 0)
|
||||
{
|
||||
ProfScope("signal") cond_var_signal(n->cv);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: threads left to enter == 0 -> last thread, wakeup
|
||||
else
|
||||
{
|
||||
ins_atomic_u64_eval_assign(&n->threads_left_to_enter, n->count);
|
||||
ProfScope("wake up") RWMutexScope(n->rw_mutex, 1)
|
||||
{
|
||||
ins_atomic_u64_eval_assign(&n->threads_left_to_leave, n->count-1);
|
||||
}
|
||||
ProfScope("signal") cond_var_signal(n->cv);
|
||||
}
|
||||
|
||||
//- rjf: wait for threads left to leave == 0
|
||||
ProfScope("wait for threads to leave")
|
||||
{
|
||||
for(U64 spin_count = 0;; spin_count += 1)
|
||||
{
|
||||
if(ins_atomic_u64_eval(&n->threads_left_to_leave) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProfEnd();
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
///~ Semaphore
|
||||
|
||||
internal void
|
||||
semaphore_drop(Semaphore semaphore)
|
||||
{
|
||||
semaphore_drop_count(semaphore, 1);
|
||||
}
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_THREADS_H
|
||||
#define BASE_THREADS_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Types
|
||||
|
||||
typedef struct Thread Thread;
|
||||
struct Thread
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
typedef void ThreadEntryPointFunctionType(void *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Synchronization Primitive Types
|
||||
|
||||
typedef struct Mutex Mutex;
|
||||
struct Mutex
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct RWMutex RWMutex;
|
||||
struct RWMutex
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct CondVar CondVar;
|
||||
struct CondVar
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct Semaphore Semaphore;
|
||||
struct Semaphore
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct Barrier Barrier;
|
||||
struct Barrier
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Table Stripes
|
||||
|
||||
typedef struct Stripe Stripe;
|
||||
struct Stripe
|
||||
{
|
||||
Arena *arena;
|
||||
RWMutex rw_mutex;
|
||||
CondVar cv;
|
||||
void *free;
|
||||
};
|
||||
|
||||
typedef struct StripeArray StripeArray;
|
||||
struct StripeArray
|
||||
{
|
||||
Stripe *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Table Stripe Functions
|
||||
|
||||
internal StripeArray stripe_array_alloc(Arena *arena);
|
||||
internal void stripe_array_release(StripeArray *stripes);
|
||||
internal Stripe *stripe_from_slot_idx(StripeArray *stripes, U64 slot_idx);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Info Helpers
|
||||
|
||||
internal void set_thread_name(String8 string);
|
||||
internal void set_thread_namef(char *fmt, ...);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Current Thread Info
|
||||
|
||||
internal U32 tid(void);
|
||||
internal void set_platform_thread_name(String8 name);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Thread Functions
|
||||
|
||||
internal Thread thread_launch(ThreadEntryPointFunctionType *f, void *p);
|
||||
internal B32 thread_join(Thread thread, U64 endt_us);
|
||||
internal void thread_detach(Thread thread);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Synchronization Primitive Functions
|
||||
|
||||
//- rjf: recursive mutexes
|
||||
internal Mutex mutex_alloc(void);
|
||||
internal void mutex_release(Mutex mutex);
|
||||
internal void mutex_take(Mutex mutex);
|
||||
internal void mutex_drop(Mutex mutex);
|
||||
|
||||
//- rjf: reader/writer mutexes
|
||||
internal RWMutex rw_mutex_alloc(void);
|
||||
internal void rw_mutex_release(RWMutex mutex);
|
||||
internal void rw_mutex_take(RWMutex mutex, B32 write_mode);
|
||||
internal void rw_mutex_drop(RWMutex mutex, B32 write_mode);
|
||||
#define rw_mutex_take_r(m) rw_mutex_take((m), (0))
|
||||
#define rw_mutex_take_w(m) rw_mutex_take((m), (1))
|
||||
#define rw_mutex_drop_r(m) rw_mutex_drop((m), (0))
|
||||
#define rw_mutex_drop_w(m) rw_mutex_drop((m), (1))
|
||||
|
||||
//- rjf: condition variables
|
||||
internal CondVar cond_var_alloc(void);
|
||||
internal void cond_var_release(CondVar cv);
|
||||
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
|
||||
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us);
|
||||
internal B32 cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us);
|
||||
#define cond_var_wait_rw_r(cv, m, endt) cond_var_wait_rw((cv), (m), (0), (endt))
|
||||
#define cond_var_wait_rw_w(cv, m, endt) cond_var_wait_rw((cv), (m), (1), (endt))
|
||||
internal void cond_var_signal(CondVar cv);
|
||||
internal void cond_var_broadcast(CondVar cv);
|
||||
|
||||
//- rjf: cross-process semaphores
|
||||
internal Semaphore semaphore_alloc(U32 initial_count, U32 max_count, String8 name);
|
||||
internal void semaphore_release(Semaphore semaphore);
|
||||
internal Semaphore semaphore_open(String8 name);
|
||||
internal void semaphore_close(Semaphore semaphore);
|
||||
internal B32 semaphore_take(Semaphore semaphore, U64 endt_us);
|
||||
internal void semaphore_drop(Semaphore semaphore);
|
||||
internal void semaphore_drop_count(Semaphore semaphore, U64 drop_count);
|
||||
|
||||
//- rjf: barriers
|
||||
internal Barrier barrier_alloc(U64 count);
|
||||
internal void barrier_release(Barrier barrier);
|
||||
internal void barrier_wait(Barrier barrier);
|
||||
|
||||
//- rjf: scope macros
|
||||
#define MutexScope(mutex) DeferLoop(mutex_take(mutex), mutex_drop(mutex))
|
||||
#define RWMutexScope(mutex, write_mode) DeferLoop(rw_mutex_take((mutex), (write_mode)), rw_mutex_drop((mutex), (write_mode)))
|
||||
#define MutexScopeR(mutex) DeferLoop(rw_mutex_take_r(mutex), rw_mutex_drop_r(mutex))
|
||||
#define MutexScopeW(mutex) DeferLoop(rw_mutex_take_w(mutex), rw_mutex_drop_w(mutex))
|
||||
#define MutexScopeRWPromote(mutex) DeferLoop((rw_mutex_drop_r(mutex), rw_mutex_take_w(mutex)), (rw_mutex_drop_w(mutex), rw_mutex_take_r(mutex)))
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Platform-Abstracted Synchronization Primitive Functions
|
||||
|
||||
//- rjf: slow barriers
|
||||
internal Barrier slow_barrier_alloc(U64 count);
|
||||
internal void slow_barrier_release(Barrier barrier);
|
||||
internal void slow_barrier_wait(Barrier barrier);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @per_os_impl Safe Calls
|
||||
|
||||
internal void safe_call(ThreadEntryPointFunctionType *func, ThreadEntryPointFunctionType *fail_handler, void *ptr);
|
||||
|
||||
#endif // BASE_THREADS_H
|
||||
@@ -1,340 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
Test(str8_list_substr)
|
||||
{
|
||||
String8List zero_list = {0};
|
||||
|
||||
{
|
||||
String8List list = {0};
|
||||
str8_list_pushf(arena, &list, "a");
|
||||
str8_list_pushf(arena, &list, "b");
|
||||
str8_list_pushf(arena, &list, "c");
|
||||
String8List sub = str8_list_substr(arena, list, r1u64(0, 3));
|
||||
String8 result = str8_list_join(arena, &list, 0);
|
||||
T_Ok(str8_match(result, str8_lit("abc"), 0));
|
||||
}
|
||||
|
||||
{
|
||||
String8List list = {0};
|
||||
str8_list_pushf(arena, &list, "a");
|
||||
str8_list_pushf(arena, &list, "b");
|
||||
str8_list_pushf(arena, &list, "c");
|
||||
String8List sub = str8_list_substr(arena, list, r1u64(0, max_U64));
|
||||
String8 result = str8_list_join(arena, &list, 0);
|
||||
T_Ok(str8_match(result, str8_lit("abc"), 0));
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
String8List list = {0};
|
||||
str8_list_pushf(arena, &list, "a");
|
||||
str8_list_pushf(arena, &list, "bcd");
|
||||
String8List sub = str8_list_substr(arena, list, r1u64(2, 3));
|
||||
String8 result = str8_list_join(arena, &sub, 0);
|
||||
T_Ok(str8_match(result, str8_lit("c"), 0));
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
String8List list = {0};
|
||||
str8_list_pushf(arena, &list, "a");
|
||||
str8_list_pushf(arena, &list, "bcd");
|
||||
String8List sub = str8_list_substr(arena, list, r1u64(1, 2));
|
||||
String8 result = str8_list_join(arena, &sub, 0);
|
||||
T_Ok(str8_match(result, str8_lit("b"), 0));
|
||||
}
|
||||
|
||||
{
|
||||
String8List list = {0};
|
||||
str8_list_pushf(arena, &list, "ab");
|
||||
str8_list_pushf(arena, &list, "cd");
|
||||
str8_list_pushf(arena, &list, "ef");
|
||||
String8List sub = str8_list_substr(arena, list, r1u64(1, 5));
|
||||
String8 result = str8_list_join(arena, &sub, 0);
|
||||
T_Ok(str8_match(result, str8_lit("bcde"), 0));
|
||||
}
|
||||
|
||||
{
|
||||
String8List list = {0};
|
||||
str8_list_pushf(arena, &list, "abc");
|
||||
|
||||
String8List zero = str8_list_substr(arena, list, r1u64(0, 0));
|
||||
T_Ok(MemoryMatchStruct(&zero, &zero_list));
|
||||
|
||||
String8List out_of_bounds_range = str8_list_substr(arena, list, r1u64(max_U64/2, max_U64));
|
||||
T_Ok(MemoryMatchStruct(&out_of_bounds_range, &zero_list));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(bit_array)
|
||||
{
|
||||
for (U64 start=0; start<32*3; start++) {
|
||||
for (U64 end=start; end<32*3; end++) {
|
||||
U32 v[3] = { 0 };
|
||||
for (U64 i=start; i<end; i++) {
|
||||
v[i/32] |= 1 << (i%32);
|
||||
}
|
||||
for (U64 lo=0; lo<32*3; lo++) {
|
||||
for (U64 hi=0; hi<32*3; hi++) {
|
||||
U64 expected_idx = Min(hi, end) - 1;
|
||||
B32 expected_r = hi <= start || lo >= end || lo >= hi || start >= end ? 0 : 1;
|
||||
U64 idx = bit_array_scan_right_to_left32((U32Array){.v=v, .count=ArrayCount(v)}, lo, hi, 1);
|
||||
B32 r = idx < hi;
|
||||
T_Ok(r == expected_r);
|
||||
if (r) {
|
||||
T_Ok(idx == expected_idx);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(count_digits)
|
||||
{
|
||||
T_Ok(count_digits_u64(0, 10) == 1);
|
||||
T_Ok(count_digits_u64(99, 10) == 2);
|
||||
T_Ok(count_digits_u64(999, 10) == 3);
|
||||
T_Ok(count_digits_u64(9999, 10) == 4);
|
||||
T_Ok(count_digits_u64(99999, 10) == 5);
|
||||
T_Ok(count_digits_u64(999999, 10) == 6);
|
||||
}
|
||||
|
||||
TEST(match_wildcard)
|
||||
{
|
||||
// empty strings
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit(""), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("**"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?*"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit(""), 0) == 0);
|
||||
|
||||
// exact
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("a"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), 0) == 0);
|
||||
|
||||
// ?
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("a?"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?b"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("??"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a?c"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("???"), 0) == 0);
|
||||
|
||||
// *
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*c"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*b*"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*c"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("b*"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("**"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a**c"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*b*c"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*a*d*"), 0) == 0);
|
||||
|
||||
T_Ok(str8_match_wildcard(str8_lit("abcd"), str8_lit("a*d"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abefcdgiescdfimde"), str8_lit("ab*cd?i*de"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("mississippi"), str8_lit("m*iss*ppi"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*b"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("aa"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit("aa"), str8_lit("a"), 0) == 0);
|
||||
|
||||
// case insensitive
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), StringMatchFlag_CaseInsensitive) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("FooBar"), str8_lit("foobar"), StringMatchFlag_CaseInsensitive) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("Foobar"), str8_lit("foo*"), StringMatchFlag_CaseInsensitive) == 1);
|
||||
|
||||
// right side sloppy
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("ab"), StringMatchFlag_RightSideSloppy) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit(""), StringMatchFlag_RightSideSloppy) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("a"), StringMatchFlag_RightSideSloppy) == 0);
|
||||
|
||||
// slash insensitive
|
||||
T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), StringMatchFlag_SlashInsensitive) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("a/b/c"), str8_lit("a\\*\\c"), StringMatchFlag_SlashInsensitive) == 1);
|
||||
|
||||
// combined
|
||||
T_Ok(str8_match_wildcard(str8_lit("Ab\\Cde"), str8_lit("ab/*e"), StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive) == 1);
|
||||
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*?*"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*?*"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?*"), 0) == 0);
|
||||
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("?*?"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?*?"), 0) == 1);
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?*?"), 0) == 0);
|
||||
}
|
||||
|
||||
TEST(hash_map)
|
||||
{
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 test_count = 256;
|
||||
for EachIndex(i, test_count) { hash_map_push_u64_u64(arena, &hm, i, i*2); }
|
||||
|
||||
// test tombstone reuse
|
||||
for (U64 i = 10; i < test_count; ++i) {
|
||||
HashMapNode *test_node = hash_map_search(&hm, hash_map_hasher(str8_struct(&i)), (HashMapKey){ .key_u64 = i }, hash_map_match_u64);
|
||||
T_Ok(hash_map_purge_u64(&hm, i) == 1);
|
||||
T_Ok(hash_map_push_u64_u64(arena, &hm, i, 10) == test_node);
|
||||
}
|
||||
|
||||
// delete some items and after search them
|
||||
for (U64 i = 0; i < 10; ++i) {
|
||||
T_Ok(hash_map_purge_u64(&hm, i));
|
||||
}
|
||||
T_Ok(hm.tombstone_count == 10);
|
||||
for (U64 i = 0; i < 10; ++i) {
|
||||
T_Ok(hash_map_search_u64_raw(&hm, i) == 0);
|
||||
}
|
||||
|
||||
// extract pairs and test sorting
|
||||
HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm);
|
||||
sort_hash_map_key_value_u64(pairs, hm.count);
|
||||
for (U64 i = 1; i < hm.count; ++i) {
|
||||
T_Ok(pairs[i-1].key.key_u64 < pairs[i].key.key_u64);
|
||||
}
|
||||
|
||||
// did purge put all the nodes on free list?
|
||||
hash_map_purge(&hm);
|
||||
U64 free_list_count = 0;
|
||||
for (HashMapNode *n = hm.free_list; n != 0; n = n->next) { free_list_count += 1; }
|
||||
T_Ok(free_list_count == test_count);
|
||||
}
|
||||
|
||||
// test search through tombstones
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 hash = 12345;
|
||||
|
||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 1 }, .value = { .value_u64 = 10 } }, hash_map_match_u64);
|
||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 2 }, .value = { .value_u64 = 20 } }, hash_map_match_u64);
|
||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 3 }, .value = { .value_u64 = 30 } }, hash_map_match_u64);
|
||||
|
||||
T_Ok(hash_map_purge_u64(&hm, 1));
|
||||
|
||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 2 }, hash_map_match_u64)->v.value.value_u64 == 20);
|
||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 3 }, hash_map_match_u64)->v.value.value_u64 == 30);
|
||||
}
|
||||
|
||||
// interleaved purges
|
||||
{
|
||||
HashMap hm = {0};
|
||||
for EachIndex(round, 100) {
|
||||
for EachIndex(i, 256) { hash_map_push_u64_u64(arena, &hm, i, round); }
|
||||
for (U64 i = 0; i < 256; i += 2) { hash_map_purge_u64(&hm, i); }
|
||||
for (U64 i = 0; i < 256; i += 2) { hash_map_push_u64_u64(arena, &hm, i, round + 1); }
|
||||
|
||||
T_Ok(hm.count == 256);
|
||||
T_Ok(hm.tombstone_count == 0);
|
||||
}
|
||||
|
||||
for EachIndex(i, 100) {
|
||||
T_Ok(hash_map_purge_u64(&hm, 123123123 + i) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// empty hash map test
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
T_Ok(hash_map_search_u64_raw(&hm, 123) == 0);
|
||||
T_Ok(hash_map_purge_u64(&hm, 123) == 0);
|
||||
|
||||
HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm);
|
||||
T_Ok(pairs == 0 || hm.count == 0);
|
||||
|
||||
hash_map_purge(&hm);
|
||||
T_Ok(hm.count == 0);
|
||||
T_Ok(hm.tombstone_count == 0);
|
||||
}
|
||||
|
||||
// heavy collision
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 hash = 12345;
|
||||
U64 count = 1024;
|
||||
|
||||
for EachIndex(i, count) {
|
||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = i }, .value = { .value_u64 = i*3 } }, hash_map_match_u64);
|
||||
}
|
||||
|
||||
for EachIndex(i, count) {
|
||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = i }, hash_map_match_u64)->v.value.value_u64 == i*3);
|
||||
}
|
||||
}
|
||||
|
||||
// duplicate key test
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 a = 1;
|
||||
U64 b = 2;
|
||||
|
||||
HashMapNode *n0 = hash_map_push_u64_u64(arena, &hm, 1, a);
|
||||
HashMapNode *n1 = hash_map_push_u64_u64(arena, &hm, 1, b);
|
||||
|
||||
T_Ok(hm.count == 1);
|
||||
T_Ok(n0 == n1);
|
||||
U64 test = *hash_map_search_u64_u64(&hm, 1);
|
||||
T_Ok(test != b);
|
||||
}
|
||||
|
||||
// test paths
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 foo = 0;
|
||||
U64 bar = 1;
|
||||
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo);
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo);
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo);
|
||||
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("/mnt/devel"), &bar);
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("/MNT/devel"), &bar);
|
||||
|
||||
T_Ok(hm.count == 2);
|
||||
|
||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:/devel/test")) == &foo);
|
||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:\\devel\\TEST")) == &foo);
|
||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("/mnt/devel")) == &bar);
|
||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("/MNT/DEVEL")) == &bar);
|
||||
}
|
||||
|
||||
// test strings
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 foo = 0;
|
||||
U64 bar = 1;
|
||||
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo);
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo);
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo);
|
||||
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("/mnt/devel"), &bar);
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("/MNT/devel"), &bar);
|
||||
|
||||
T_Ok(hm.count == 5);
|
||||
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/DEVEL/test")) == &foo);
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:\\DEVEL\\test")) == &foo);
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/devel\\test")) == &foo);
|
||||
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("/mnt/devel")) == &bar);
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("/MNT/devel")) == &bar);
|
||||
}
|
||||
}
|
||||
+507
-1071
File diff suppressed because it is too large
Load Diff
+2938
-2768
File diff suppressed because it is too large
Load Diff
+622
-630
File diff suppressed because it is too large
Load Diff
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal String8
|
||||
cv_string_from_numeric(Arena *arena, CV_NumericParsed num)
|
||||
{
|
||||
String8 result = str8_zero();
|
||||
switch (num.kind) {
|
||||
case CV_NumericKind_FLOAT16: NotImplemented; break; // TODO: format float16
|
||||
case CV_NumericKind_FLOAT32: result = push_str8f(arena, "%f", (F64)(*(F32*)num.val)); break;
|
||||
case CV_NumericKind_FLOAT48: NotImplemented; break; // TODO: format float48
|
||||
case CV_NumericKind_FLOAT64: result = push_str8f(arena, "%f", *(F64*)num.val); break;
|
||||
case CV_NumericKind_FLOAT80: NotImplemented; break; // TODO: format float80
|
||||
case CV_NumericKind_FLOAT128: NotImplemented; break; // TODO: format float128
|
||||
case CV_NumericKind_CHAR: result = push_str8f(arena, "%d", *(S8 *)num.val); break;
|
||||
case CV_NumericKind_SHORT: result = push_str8f(arena, "%d", *(S16*)num.val); break;
|
||||
case CV_NumericKind_LONG: result = push_str8f(arena, "%d", *(S32*)num.val); break;
|
||||
case CV_NumericKind_QUADWORD: result = push_str8f(arena, "%lld", *(S64*)num.val); break;
|
||||
case CV_NumericKind_USHORT: result = push_str8f(arena, "%u", *(U16*)num.val); break;
|
||||
case CV_NumericKind_ULONG: result = push_str8f(arena, "%u", *(U32*)num.val); break;
|
||||
case CV_NumericKind_UQUADWORD: result = push_str8f(arena, "%llu", *(U64*)num.val); break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CODEVIEW_DUMP_H
|
||||
#define CODEVIEW_DUMP_H
|
||||
|
||||
internal String8 cv_string_from_numeric(Arena *arena, CV_NumericParsed num);
|
||||
|
||||
#endif // CODEVIEW_DUMP_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,320 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CODEVIEW_PARSE_H
|
||||
#define CODEVIEW_PARSE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Common Parser Types
|
||||
|
||||
// CV_Numeric layout
|
||||
// x: U16
|
||||
// buf: U8[]
|
||||
// case (x < 0x8000): kind=U16 val=x
|
||||
// case (x >= 0x8000): kind=x val=buf
|
||||
|
||||
typedef struct CV_NumericParsed CV_NumericParsed;
|
||||
struct CV_NumericParsed
|
||||
{
|
||||
CV_NumericKind kind;
|
||||
U8 *val;
|
||||
U64 encoded_size;
|
||||
};
|
||||
|
||||
typedef struct CV_RecRange CV_RecRange;
|
||||
struct CV_RecRange
|
||||
{
|
||||
U32 off;
|
||||
CV_RecHeader hdr;
|
||||
};
|
||||
|
||||
#define CV_REC_RANGE_CHUNK_SIZE 511
|
||||
|
||||
typedef struct CV_RecRangeChunk CV_RecRangeChunk;
|
||||
struct CV_RecRangeChunk
|
||||
{
|
||||
struct CV_RecRangeChunk *next;
|
||||
CV_RecRange ranges[CV_REC_RANGE_CHUNK_SIZE];
|
||||
};
|
||||
|
||||
typedef struct CV_RecRangeStream CV_RecRangeStream;
|
||||
struct CV_RecRangeStream
|
||||
{
|
||||
CV_RecRangeChunk *first_chunk;
|
||||
CV_RecRangeChunk *last_chunk;
|
||||
U64 total_count;
|
||||
};
|
||||
|
||||
typedef struct CV_RecRangeArray CV_RecRangeArray;
|
||||
struct CV_RecRangeArray
|
||||
{
|
||||
CV_RecRange *ranges;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Sym Parser Types
|
||||
|
||||
typedef struct CV_SymTopLevelInfo CV_SymTopLevelInfo;
|
||||
struct CV_SymTopLevelInfo
|
||||
{
|
||||
CV_Arch arch;
|
||||
CV_Language language;
|
||||
String8 compiler_name;
|
||||
};
|
||||
|
||||
typedef struct CV_SymParsed CV_SymParsed;
|
||||
struct CV_SymParsed
|
||||
{
|
||||
// source information
|
||||
String8 data;
|
||||
U64 sym_align;
|
||||
|
||||
// sym index derived from source
|
||||
CV_RecRangeArray sym_ranges;
|
||||
|
||||
// top-level info derived from the syms
|
||||
CV_SymTopLevelInfo info;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Leaf Parser Types
|
||||
|
||||
typedef struct CV_LeafParsed CV_LeafParsed;
|
||||
struct CV_LeafParsed
|
||||
{
|
||||
// source information
|
||||
String8 data;
|
||||
CV_TypeId itype_first;
|
||||
CV_TypeId itype_opl;
|
||||
|
||||
// leaf index derived from source
|
||||
CV_RecRangeArray leaf_ranges;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView C13 Info Parser Types
|
||||
|
||||
typedef struct CV_C13InlineSiteDecoder CV_C13InlineSiteDecoder;
|
||||
struct CV_C13InlineSiteDecoder
|
||||
{
|
||||
U64 cursor;
|
||||
U64 parent_voff;
|
||||
CV_InlineRangeKind range_kind;
|
||||
U32 code_length;
|
||||
U32 code_offset;
|
||||
U32 file_off;
|
||||
S32 ln;
|
||||
S32 cn;
|
||||
U64 code_offset_lo;
|
||||
B32 code_offset_changed;
|
||||
B32 code_offset_lo_changed;
|
||||
B32 code_length_changed;
|
||||
B32 ln_changed;
|
||||
B32 file_off_changed;
|
||||
Rng1U64 last_range;
|
||||
U32 file_count;
|
||||
Rng1U64 file_last_range;
|
||||
U64 file_line_count;
|
||||
U64 file_last_ln;
|
||||
};
|
||||
|
||||
typedef U32 CV_C13InlineSiteDecoderStepFlags;
|
||||
enum
|
||||
{
|
||||
CV_C13InlineSiteDecoderStepFlag_EmitRange = (1 << 0),
|
||||
CV_C13InlineSiteDecoderStepFlag_ExtendLastRange = (1 << 1),
|
||||
CV_C13InlineSiteDecoderStepFlag_EmitFile = (1 << 2),
|
||||
CV_C13InlineSiteDecoderStepFlag_EmitLine = (1 << 3),
|
||||
};
|
||||
|
||||
typedef struct CV_C13InlineSiteDecoderStep CV_C13InlineSiteDecoderStep;
|
||||
struct CV_C13InlineSiteDecoderStep
|
||||
{
|
||||
CV_C13InlineSiteDecoderStepFlags flags;
|
||||
Rng1U64 range;
|
||||
U64 line_voff;
|
||||
U64 line_voff_end;
|
||||
U64 ln;
|
||||
U64 cn;
|
||||
U32 file_off;
|
||||
};
|
||||
|
||||
typedef struct CV_C13LinesParsed CV_C13LinesParsed;
|
||||
struct CV_C13LinesParsed
|
||||
{
|
||||
// raw info
|
||||
U32 sec_idx;
|
||||
U32 file_off;
|
||||
U64 secrel_base_off;
|
||||
|
||||
// parsed info
|
||||
String8 file_name;
|
||||
CV_C13ChecksumKind checksum_kind;
|
||||
String8 checksum;
|
||||
U64 *voffs; // [line_count + 1]
|
||||
U32 *line_nums; // [line_count]
|
||||
U16 *col_nums; // [2*line_count]
|
||||
U32 line_count;
|
||||
};
|
||||
|
||||
typedef struct CV_C13LinesParsedNode CV_C13LinesParsedNode;
|
||||
struct CV_C13LinesParsedNode
|
||||
{
|
||||
CV_C13LinesParsedNode *next;
|
||||
CV_C13LinesParsed v;
|
||||
};
|
||||
|
||||
typedef struct CV_C13InlineeLinesParsed CV_C13InlineeLinesParsed;
|
||||
struct CV_C13InlineeLinesParsed
|
||||
{
|
||||
CV_ItemId inlinee;
|
||||
U32 file_off;
|
||||
String8 file_name;
|
||||
CV_C13ChecksumKind checksum_kind;
|
||||
String8 checksum;
|
||||
U32 first_source_ln;
|
||||
U32 extra_file_count;
|
||||
U32 *extra_files;
|
||||
};
|
||||
|
||||
typedef struct CV_C13InlineeLinesParsedNode CV_C13InlineeLinesParsedNode;
|
||||
struct CV_C13InlineeLinesParsedNode
|
||||
{
|
||||
CV_C13InlineeLinesParsedNode *next;
|
||||
CV_C13InlineeLinesParsedNode *hash_next;
|
||||
CV_C13InlineeLinesParsed v;
|
||||
};
|
||||
|
||||
typedef struct CV_C13SubSectionNode CV_C13SubSectionNode;
|
||||
struct CV_C13SubSectionNode
|
||||
{
|
||||
struct CV_C13SubSectionNode *next;
|
||||
CV_C13SubSectionKind kind;
|
||||
U32 off;
|
||||
U32 size;
|
||||
CV_C13LinesParsedNode *lines_first;
|
||||
CV_C13LinesParsedNode *lines_last;
|
||||
CV_C13InlineeLinesParsedNode *inlinee_lines_first;
|
||||
CV_C13InlineeLinesParsedNode *inlinee_lines_last;
|
||||
};
|
||||
|
||||
typedef struct CV_C13Parsed CV_C13Parsed;
|
||||
struct CV_C13Parsed
|
||||
{
|
||||
// rjf: source data
|
||||
String8 data;
|
||||
|
||||
// rjf: full sub-section list
|
||||
CV_C13SubSectionNode *first_sub_section;
|
||||
CV_C13SubSectionNode *last_sub_section;
|
||||
U64 sub_section_count;
|
||||
|
||||
// rjf: fastpath to file checksums section
|
||||
CV_C13SubSectionNode *file_chksms_sub_section;
|
||||
|
||||
// rjf: fastpath to map inlinee CV_ItemId -> CV_InlineeLinesParsed quickly
|
||||
CV_C13InlineeLinesParsedNode **inlinee_lines_parsed_slots;
|
||||
U64 inlinee_lines_parsed_slots_count;
|
||||
};
|
||||
|
||||
typedef struct CV_UDTInfo CV_UDTInfo;
|
||||
struct CV_UDTInfo
|
||||
{
|
||||
String8 name;
|
||||
String8 unique_name;
|
||||
CV_TypeProps props;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Compound Types
|
||||
|
||||
typedef struct CV_TypeIdArray CV_TypeIdArray;
|
||||
struct CV_TypeIdArray
|
||||
{
|
||||
CV_TypeId *itypes;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ Record Iterator
|
||||
|
||||
typedef struct CV_RecIter CV_RecIter;
|
||||
struct CV_RecIter
|
||||
{
|
||||
U64 num;
|
||||
U16 kind;
|
||||
void *struct_base;
|
||||
void *opl;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
//- Hasher
|
||||
|
||||
internal U64 cv_hash_from_string(String8 string);
|
||||
internal U64 cv_hash_from_item_id(CV_ItemId item_id);
|
||||
|
||||
//- Numeric Decoder
|
||||
|
||||
internal CV_NumericParsed cv_numeric_from_data_range(U8 *first, U8 *opl);
|
||||
internal U64 cv_read_numeric(String8 data, U64 offset, CV_NumericParsed *out);
|
||||
|
||||
internal B32 cv_numeric_fits_in_u64(CV_NumericParsed *num);
|
||||
internal B32 cv_numeric_fits_in_s64(CV_NumericParsed *num);
|
||||
internal B32 cv_numeric_fits_in_f64(CV_NumericParsed *num);
|
||||
|
||||
internal U64 cv_u64_from_numeric(CV_NumericParsed *num);
|
||||
internal S64 cv_s64_from_numeric(CV_NumericParsed *num);
|
||||
internal F64 cv_f64_from_numeric(CV_NumericParsed *num);
|
||||
|
||||
//- Inlinee Lines Binary Annot Decoder
|
||||
|
||||
internal U64 cv_decode_inline_annot_u32(String8 data, U64 offset, U32 *out_value);
|
||||
internal U64 cv_decode_inline_annot_s32(String8 data, U64 offset, S32 *out_value);
|
||||
internal S32 cv_inline_annot_signed_from_unsigned_operand(U32 value);
|
||||
|
||||
internal CV_C13InlineSiteDecoder cv_c13_inline_site_decoder_init(U32 file_off, U32 first_source_ln, U32 parent_voff);
|
||||
internal CV_C13InlineSiteDecoderStep cv_c13_inline_site_decoder_step(CV_C13InlineSiteDecoder *decoder, String8 binary_annots);
|
||||
|
||||
//- Symbol/Leaf Helpers
|
||||
|
||||
internal B32 cv_is_udt_name_anon(String8 name);
|
||||
|
||||
internal B32 cv_is_global_symbol(CV_SymKind kind);
|
||||
internal B32 cv_is_typedef(CV_SymKind kind);
|
||||
internal B32 cv_is_scope_symbol(CV_SymKind kind);
|
||||
internal B32 cv_is_end_symbol(CV_SymKind kind);
|
||||
|
||||
internal B32 cv_is_udt(CV_LeafKind kind);
|
||||
internal B32 cv_is_leaf_type_server(CV_LeafKind kind);
|
||||
internal B32 cv_is_leaf_pch(CV_LeafKind kind);
|
||||
internal CV_TypeIndexSource cv_type_index_source_from_leaf_kind(CV_LeafKind leaf_kind);
|
||||
|
||||
internal CV_TypeIndexInfoList cv_get_symbol_type_index_offsets(Arena *arena, CV_SymKind kind, String8 data);
|
||||
internal CV_TypeIndexInfoList cv_get_leaf_type_index_offsets(Arena *arena, CV_LeafKind leaf_kind, String8 data);
|
||||
internal CV_TypeIndexInfoList cv_get_inlinee_type_index_offsets(Arena *arena, String8 raw_data);
|
||||
internal String8Array cv_get_data_around_type_indices(Arena *arena, CV_TypeIndexInfoList ti_list, String8 data);
|
||||
|
||||
internal U64 cv_name_offset_from_symbol(CV_SymKind kind, String8 data);
|
||||
internal String8 cv_name_from_symbol(CV_SymKind kind, String8 data);
|
||||
|
||||
internal CV_UDTInfo cv_get_udt_info(CV_LeafKind kind, String8 data);
|
||||
internal String8 cv_name_from_udt_info(CV_UDTInfo udt_info);
|
||||
|
||||
//- rjf: record range stream parsing
|
||||
internal CV_RecRangeStream * cv_rec_range_stream_from_data(Arena *arena, String8 data, U64 align);
|
||||
internal CV_RecRangeArray cv_rec_range_array_from_stream(Arena *arena, CV_RecRangeStream *stream);
|
||||
|
||||
//- rjf: sym stream parsing
|
||||
internal CV_SymParsed * cv_sym_from_data(Arena *arena, String8 sym_data, U64 sym_align);
|
||||
|
||||
//- rjf: leaf stream parsing
|
||||
internal CV_LeafParsed * cv_leaf_from_data(Arena *arena, String8 leaf_data, CV_TypeId first);
|
||||
internal CV_C13Parsed * cv_c13_parsed_from_data(Arena *arena, String8 c13_data, String8 strtbl, COFF_SectionHeaderArray sections);
|
||||
|
||||
//- rjf: record iterator
|
||||
internal B32 cv_rec_next(String8 data, CV_RecRangeArray *recs, B32 is_leaf, CV_RecIter *iter);
|
||||
|
||||
#endif // CODEVIEW_PARSE_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CODEVIEW_STRINGIZE_H
|
||||
#define CODEVIEW_STRINGIZE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Stringize Helper Types
|
||||
|
||||
typedef struct CV_StringizeSymParams{
|
||||
CV_Arch arch;
|
||||
} CV_StringizeSymParams;
|
||||
|
||||
typedef struct CV_StringizeLeafParams{
|
||||
U32 dummy;
|
||||
} CV_StringizeLeafParams;
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Common Stringize Functions
|
||||
|
||||
internal void cv_stringize_numeric(Arena *arena, String8List *out, CV_NumericParsed *num);
|
||||
|
||||
internal void cv_stringize_lvar_addr_range(Arena *arena, String8List *out,
|
||||
CV_LvarAddrRange *range);
|
||||
internal void cv_stringize_lvar_addr_gap(Arena *arena, String8List *out, CV_LvarAddrGap *gap);
|
||||
internal void cv_stringize_lvar_addr_gap_list(Arena *arena, String8List *out,
|
||||
void *first, void *opl);
|
||||
|
||||
internal String8 cv_string_from_basic_type(CV_BasicType basic_type);
|
||||
internal String8 cv_string_from_c13_sub_section_kind(CV_C13_SubSectionKind kind);
|
||||
internal String8 cv_string_from_reg(CV_Arch arch, CV_Reg reg);
|
||||
internal String8 cv_string_from_pointer_kind(CV_PointerKind ptr_kind);
|
||||
internal String8 cv_string_from_pointer_mode(CV_PointerMode ptr_mode);
|
||||
internal String8 cv_string_from_hfa_kind(CV_HFAKind hfa_kind);
|
||||
internal String8 cv_string_from_mo_com_udt_kind(CV_MoComUDTKind mo_com_udt_kind);
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Flags Stringize Functions
|
||||
|
||||
internal void cv_stringize_modifier_flags(Arena *arena, String8List *out,
|
||||
U32 indent, CV_ModifierFlags flags);
|
||||
|
||||
internal void cv_stringize_type_props(Arena *arena, String8List *out,
|
||||
U32 indent, CV_TypeProps props);
|
||||
|
||||
internal void cv_stringize_pointer_attribs(Arena *arena, String8List *out,
|
||||
U32 indent, CV_PointerAttribs attribs);
|
||||
|
||||
internal void cv_stringize_local_flags(Arena *arena, String8List *out,
|
||||
U32 indent, CV_LocalFlags flags);
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Sym Stringize Functions
|
||||
|
||||
internal void cv_stringize_sym_parsed(Arena *arena, String8List *out, CV_SymParsed *sym);
|
||||
|
||||
internal void cv_stringize_sym_range(Arena *arena, String8List *out,
|
||||
CV_RecRange *range, String8 data,
|
||||
CV_StringizeSymParams *p);
|
||||
internal void cv_stringize_sym_array(Arena *arena, String8List *out,
|
||||
CV_RecRangeArray *ranges, String8 data,
|
||||
CV_StringizeSymParams *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView Leaf Stringize Functions
|
||||
|
||||
internal void cv_stringize_leaf_parsed(Arena *arena, String8List *out, CV_LeafParsed *leaf);
|
||||
|
||||
internal void cv_stringize_leaf_range(Arena *arena, String8List *out,
|
||||
CV_RecRange *range, CV_TypeId itype, String8 data,
|
||||
CV_StringizeLeafParams *p);
|
||||
internal void cv_stringize_leaf_array(Arena *arena, String8List *out,
|
||||
CV_RecRangeArray *ranges, CV_TypeId itype_first,
|
||||
String8 data,
|
||||
CV_StringizeLeafParams *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ CodeView C13 Stringize Functions
|
||||
|
||||
internal void cv_stringize_c13_parsed(Arena *arena, String8List *out, CV_C13Parsed *c13);
|
||||
|
||||
#endif // CODEVIEW_STRINGIZE_H
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
@@ -392,48 +392,48 @@ case CV_BasicType_NBASICSTR:{result = str8_lit("");}break;
|
||||
case CV_BasicType_FBASICSTR:{result = str8_lit("");}break;
|
||||
case CV_BasicType_NOTTRANS:{result = str8_lit("");}break;
|
||||
case CV_BasicType_HRESULT:{result = str8_lit("HRESULT");}break;
|
||||
case CV_BasicType_CHAR:{result = str8_lit("CHAR");}break;
|
||||
case CV_BasicType_SHORT:{result = str8_lit("SHORT");}break;
|
||||
case CV_BasicType_LONG:{result = str8_lit("LONG");}break;
|
||||
case CV_BasicType_QUAD:{result = str8_lit("QUAD");}break;
|
||||
case CV_BasicType_OCT:{result = str8_lit("OCT");}break;
|
||||
case CV_BasicType_CHAR:{result = str8_lit("char");}break;
|
||||
case CV_BasicType_SHORT:{result = str8_lit("S16");}break;
|
||||
case CV_BasicType_LONG:{result = str8_lit("S32");}break;
|
||||
case CV_BasicType_QUAD:{result = str8_lit("S64");}break;
|
||||
case CV_BasicType_OCT:{result = str8_lit("S128");}break;
|
||||
case CV_BasicType_UCHAR:{result = str8_lit("UCHAR");}break;
|
||||
case CV_BasicType_USHORT:{result = str8_lit("USHORT");}break;
|
||||
case CV_BasicType_ULONG:{result = str8_lit("ULONG");}break;
|
||||
case CV_BasicType_UQUAD:{result = str8_lit("UQUAD");}break;
|
||||
case CV_BasicType_UOCT:{result = str8_lit("UOCT");}break;
|
||||
case CV_BasicType_BOOL8:{result = str8_lit("BOOL8");}break;
|
||||
case CV_BasicType_BOOL16:{result = str8_lit("BOOL16");}break;
|
||||
case CV_BasicType_BOOL32:{result = str8_lit("BOOL32");}break;
|
||||
case CV_BasicType_BOOL64:{result = str8_lit("BOOL64");}break;
|
||||
case CV_BasicType_FLOAT32:{result = str8_lit("FLOAT32");}break;
|
||||
case CV_BasicType_FLOAT64:{result = str8_lit("FLOAT64");}break;
|
||||
case CV_BasicType_FLOAT80:{result = str8_lit("FLOAT80");}break;
|
||||
case CV_BasicType_FLOAT128:{result = str8_lit("FLOAT128");}break;
|
||||
case CV_BasicType_FLOAT48:{result = str8_lit("FLOAT48");}break;
|
||||
case CV_BasicType_FLOAT32PP:{result = str8_lit("FLOAT32PP");}break;
|
||||
case CV_BasicType_FLOAT16:{result = str8_lit("FLOAT16");}break;
|
||||
case CV_BasicType_USHORT:{result = str8_lit("U16");}break;
|
||||
case CV_BasicType_ULONG:{result = str8_lit("U32");}break;
|
||||
case CV_BasicType_UQUAD:{result = str8_lit("U64");}break;
|
||||
case CV_BasicType_UOCT:{result = str8_lit("U128");}break;
|
||||
case CV_BasicType_BOOL8:{result = str8_lit("B8");}break;
|
||||
case CV_BasicType_BOOL16:{result = str8_lit("B16");}break;
|
||||
case CV_BasicType_BOOL32:{result = str8_lit("B32");}break;
|
||||
case CV_BasicType_BOOL64:{result = str8_lit("B64");}break;
|
||||
case CV_BasicType_FLOAT32:{result = str8_lit("F32");}break;
|
||||
case CV_BasicType_FLOAT64:{result = str8_lit("F64");}break;
|
||||
case CV_BasicType_FLOAT80:{result = str8_lit("F80");}break;
|
||||
case CV_BasicType_FLOAT128:{result = str8_lit("F128");}break;
|
||||
case CV_BasicType_FLOAT48:{result = str8_lit("F48");}break;
|
||||
case CV_BasicType_FLOAT32PP:{result = str8_lit("F32PP");}break;
|
||||
case CV_BasicType_FLOAT16:{result = str8_lit("F16");}break;
|
||||
case CV_BasicType_COMPLEX32:{result = str8_lit("ComplexF32");}break;
|
||||
case CV_BasicType_COMPLEX64:{result = str8_lit("ComplexF64");}break;
|
||||
case CV_BasicType_COMPLEX80:{result = str8_lit("ComplexF80");}break;
|
||||
case CV_BasicType_COMPLEX128:{result = str8_lit("ComplexF128");}break;
|
||||
case CV_BasicType_BIT:{result = str8_lit("");}break;
|
||||
case CV_BasicType_PASCHAR:{result = str8_lit("");}break;
|
||||
case CV_BasicType_BOOL32FF:{result = str8_lit("BOOL32FF");}break;
|
||||
case CV_BasicType_INT8:{result = str8_lit("int8");}break;
|
||||
case CV_BasicType_UINT8:{result = str8_lit("uint8");}break;
|
||||
case CV_BasicType_BOOL32FF:{result = str8_lit("B32FF");}break;
|
||||
case CV_BasicType_INT8:{result = str8_lit("S8");}break;
|
||||
case CV_BasicType_UINT8:{result = str8_lit("U8");}break;
|
||||
case CV_BasicType_RCHAR:{result = str8_lit("char");}break;
|
||||
case CV_BasicType_WCHAR:{result = str8_lit("WCHAR");}break;
|
||||
case CV_BasicType_INT16:{result = str8_lit("int16");}break;
|
||||
case CV_BasicType_UINT16:{result = str8_lit("uint16");}break;
|
||||
case CV_BasicType_INT32:{result = str8_lit("int32");}break;
|
||||
case CV_BasicType_UINT32:{result = str8_lit("uint32");}break;
|
||||
case CV_BasicType_INT64:{result = str8_lit("int64");}break;
|
||||
case CV_BasicType_UINT64:{result = str8_lit("uint64");}break;
|
||||
case CV_BasicType_INT128:{result = str8_lit("int128");}break;
|
||||
case CV_BasicType_UINT128:{result = str8_lit("uint128");}break;
|
||||
case CV_BasicType_CHAR16:{result = str8_lit("char16");}break;
|
||||
case CV_BasicType_CHAR32:{result = str8_lit("char32");}break;
|
||||
case CV_BasicType_INT16:{result = str8_lit("S16");}break;
|
||||
case CV_BasicType_UINT16:{result = str8_lit("U16");}break;
|
||||
case CV_BasicType_INT32:{result = str8_lit("S32");}break;
|
||||
case CV_BasicType_UINT32:{result = str8_lit("U32");}break;
|
||||
case CV_BasicType_INT64:{result = str8_lit("S64");}break;
|
||||
case CV_BasicType_UINT64:{result = str8_lit("U64");}break;
|
||||
case CV_BasicType_INT128:{result = str8_lit("S128");}break;
|
||||
case CV_BasicType_UINT128:{result = str8_lit("U128");}break;
|
||||
case CV_BasicType_CHAR16:{result = str8_lit("CHAR16");}break;
|
||||
case CV_BasicType_CHAR32:{result = str8_lit("CHAR32");}break;
|
||||
case CV_BasicType_CHAR8:{result = str8_lit("char");}break;
|
||||
case CV_BasicType_PTR:{result = str8_lit("PTR");}break;
|
||||
}
|
||||
@@ -447,7 +447,6 @@ String8 result = str8_lit("<Unknown CV_LeafKind>");
|
||||
switch(v)
|
||||
{
|
||||
default:{}break;
|
||||
case CV_LeafKind_NOTYPE:{result = str8_lit("NOTYPE");}break;
|
||||
case CV_LeafKind_MODIFIER_16t:{result = str8_lit("MODIFIER_16t");}break;
|
||||
case CV_LeafKind_POINTER_16t:{result = str8_lit("POINTER_16t");}break;
|
||||
case CV_LeafKind_ARRAY_16t:{result = str8_lit("ARRAY_16t");}break;
|
||||
@@ -572,13 +571,6 @@ case CV_LeafKind_BINTERFACE:{result = str8_lit("BINTERFACE");}break;
|
||||
case CV_LeafKind_VECTOR:{result = str8_lit("VECTOR");}break;
|
||||
case CV_LeafKind_MATRIX:{result = str8_lit("MATRIX");}break;
|
||||
case CV_LeafKind_VFTABLE:{result = str8_lit("VFTABLE");}break;
|
||||
case CV_LeafKind_FUNC_ID:{result = str8_lit("FUNC_ID");}break;
|
||||
case CV_LeafKind_MFUNC_ID:{result = str8_lit("MFUNC_ID");}break;
|
||||
case CV_LeafKind_BUILDINFO:{result = str8_lit("BUILDINFO");}break;
|
||||
case CV_LeafKind_SUBSTR_LIST:{result = str8_lit("SUBSTR_LIST");}break;
|
||||
case CV_LeafKind_STRING_ID:{result = str8_lit("STRING_ID");}break;
|
||||
case CV_LeafKind_UDT_SRC_LINE:{result = str8_lit("UDT_SRC_LINE");}break;
|
||||
case CV_LeafKind_UDT_MOD_SRC_LINE:{result = str8_lit("UDT_MOD_SRC_LINE");}break;
|
||||
case CV_LeafKind_CLASS2:{result = str8_lit("CLASS2");}break;
|
||||
case CV_LeafKind_STRUCT2:{result = str8_lit("STRUCT2");}break;
|
||||
}
|
||||
@@ -600,7 +592,7 @@ case CV_SymKind_OEM:{result = sizeof(CV_SymOEM);}break;
|
||||
case CV_SymKind_VFTABLE32:{result = sizeof(CV_SymVPath32);}break;
|
||||
case CV_SymKind_FRAMEPROC:{result = sizeof(CV_SymFrameproc);}break;
|
||||
case CV_SymKind_ANNOTATION:{result = sizeof(CV_SymAnnotation);}break;
|
||||
case CV_SymKind_OBJNAME:{result = sizeof(CV_SymObjName);}break;
|
||||
case CV_SymKind_OBJNAME:{result = sizeof(CV_SymObjname);}break;
|
||||
case CV_SymKind_THUNK32:{result = sizeof(CV_SymThunk32);}break;
|
||||
case CV_SymKind_BLOCK32:{result = sizeof(CV_SymBlock32);}break;
|
||||
case CV_SymKind_LABEL32:{result = sizeof(CV_SymLabel32);}break;
|
||||
@@ -704,15 +696,11 @@ case CV_LeafKind_ONEMETHOD:{result = sizeof(CV_LeafOneMethod);}break;
|
||||
case CV_LeafKind_NESTTYPEEX:{result = sizeof(CV_LeafNestTypeEx);}break;
|
||||
case CV_LeafKind_TYPESERVER2:{result = sizeof(CV_LeafTypeServer2);}break;
|
||||
case CV_LeafKind_INTERFACE:{result = sizeof(CV_LeafStruct);}break;
|
||||
case CV_LeafKind_FUNC_ID:{result = sizeof(CV_LeafFuncId);}break;
|
||||
case CV_LeafKind_MFUNC_ID:{result = sizeof(CV_LeafMFuncId);}break;
|
||||
case CV_LeafKind_BUILDINFO:{result = sizeof(CV_LeafBuildInfo);}break;
|
||||
case CV_LeafKind_SUBSTR_LIST:{result = sizeof(CV_LeafSubstrList);}break;
|
||||
case CV_LeafKind_STRING_ID:{result = sizeof(CV_LeafStringId);}break;
|
||||
case CV_LeafKind_UDT_SRC_LINE:{result = sizeof(CV_LeafUDTSrcLine);}break;
|
||||
case CV_LeafKind_UDT_MOD_SRC_LINE:{result = sizeof(CV_LeafUDTModSrcLine);}break;
|
||||
case CV_LeafKind_CLASS2:{result = sizeof(CV_LeafStruct2);}break;
|
||||
case CV_LeafKind_STRUCT2:{result = sizeof(CV_LeafStruct2);}break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
C_LINKAGE_BEGIN
|
||||
C_LINKAGE_END
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
@@ -381,7 +381,6 @@ CV_BasicType_PTR = 0xf0,
|
||||
typedef U16 CV_LeafKind;
|
||||
typedef enum CV_LeafKindEnum
|
||||
{
|
||||
CV_LeafKind_NOTYPE = 0x0000,
|
||||
CV_LeafKind_MODIFIER_16t = 0x0001,
|
||||
CV_LeafKind_POINTER_16t = 0x0002,
|
||||
CV_LeafKind_ARRAY_16t = 0x0003,
|
||||
@@ -506,13 +505,6 @@ CV_LeafKind_BINTERFACE = 0x151a,
|
||||
CV_LeafKind_VECTOR = 0x151b,
|
||||
CV_LeafKind_MATRIX = 0x151c,
|
||||
CV_LeafKind_VFTABLE = 0x151d,
|
||||
CV_LeafKind_FUNC_ID = 0x1601,
|
||||
CV_LeafKind_MFUNC_ID = 0x1602,
|
||||
CV_LeafKind_BUILDINFO = 0x1603,
|
||||
CV_LeafKind_SUBSTR_LIST = 0x1604,
|
||||
CV_LeafKind_STRING_ID = 0x1605,
|
||||
CV_LeafKind_UDT_SRC_LINE = 0x1606,
|
||||
CV_LeafKind_UDT_MOD_SRC_LINE = 0x1607,
|
||||
CV_LeafKind_CLASS2 = 0x1608,
|
||||
CV_LeafKind_STRUCT2 = 0x1609,
|
||||
} CV_LeafKindEnum;
|
||||
@@ -525,4 +517,7 @@ internal String8 cv_type_name_from_basic_type(CV_BasicType v);
|
||||
internal String8 cv_string_from_leaf_kind(CV_LeafKind v);
|
||||
internal U64 cv_header_struct_size_from_sym_kind(CV_SymKind v);
|
||||
internal U64 cv_header_struct_size_from_leaf_kind(CV_LeafKind v);
|
||||
C_LINKAGE_BEGIN
|
||||
C_LINKAGE_END
|
||||
|
||||
#endif // CODEVIEW_META_H
|
||||
|
||||
+1119
-964
File diff suppressed because it is too large
Load Diff
+719
-499
File diff suppressed because it is too large
Load Diff
@@ -1,819 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#if 0
|
||||
internal void
|
||||
coff_print_archive_member_header(Arena *arena, String8List *out, String8 indent, COFF_ParsedArchiveMemberHeader header, String8 long_names)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8 time_stamp = coff_string_from_time_stamp(scratch.arena, header.time_stamp);
|
||||
|
||||
rd_printf("Name : %S" , header.name );
|
||||
rd_printf("Time Stamp: (%#x) %S" , header.time_stamp, time_stamp );
|
||||
rd_printf("User ID : %u" , header.user_id );
|
||||
rd_printf("Group ID : %u" , header.group_id);
|
||||
rd_printf("Mode : %S" , header.mode );
|
||||
rd_printf("Data : [%#llx-%#llx)", header.data_range.min, header.data_range.max);
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_section_table(Arena *arena,
|
||||
String8List *out,
|
||||
String8 indent,
|
||||
String8 string_table,
|
||||
COFF_Symbol32Array symbol_table,
|
||||
U64 section_count,
|
||||
COFF_SectionHeader *section_table)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
String8 *symlinks = push_array(scratch.arena, String8, section_count);
|
||||
for (U64 i = 0; i < symbol_table.count; ++i) {
|
||||
COFF_Symbol32 *symbol = symbol_table.v+i;
|
||||
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol->section_number, symbol->value, symbol->storage_class);
|
||||
if (interp == COFF_SymbolValueInterp_Regular &&
|
||||
symbol->aux_symbol_count == 0 &&
|
||||
(symbol->storage_class == COFF_SymStorageClass_External || symbol->storage_class == COFF_SymStorageClass_Static)) {
|
||||
if (symbol->section_number > 0 && symbol->section_number <= symbol_table.count) {
|
||||
COFF_SectionHeader *header = section_table+(symbol->section_number-1);
|
||||
if (header->flags & COFF_SectionFlag_LnkCOMDAT) {
|
||||
symlinks[symbol->section_number-1] = coff_read_symbol_name(string_table, &symbol->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
i += symbol->aux_symbol_count;
|
||||
}
|
||||
|
||||
if (section_count) {
|
||||
rd_printf("# Section Table");
|
||||
rd_indent();
|
||||
|
||||
rd_printf("%-4s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-5s %-10s %s",
|
||||
"No.",
|
||||
"Name",
|
||||
"VirtSize",
|
||||
"VirtOff",
|
||||
"FileSize",
|
||||
"FileOff",
|
||||
"RelocOff",
|
||||
"LinesOff",
|
||||
"RelocCnt",
|
||||
"LineCnt",
|
||||
"Align",
|
||||
"Flags",
|
||||
"Symlink");
|
||||
|
||||
for (U64 i = 0; i < section_count; ++i) {
|
||||
COFF_SectionHeader *header = section_table+i;
|
||||
|
||||
String8 name = str8_cstring_capped(header->name, header->name+sizeof(header->name));
|
||||
String8 full_name = coff_name_from_section_header(string_table, header);
|
||||
|
||||
String8 align;
|
||||
{
|
||||
U64 align_size = coff_align_size_from_section_flags(header->flags);
|
||||
align = push_str8f(scratch.arena, "%u", align_size);
|
||||
}
|
||||
|
||||
String8 flags;
|
||||
{
|
||||
String8List mem_flags = {0};
|
||||
if (header->flags & COFF_SectionFlag_MemRead) {
|
||||
str8_list_pushf(scratch.arena, &mem_flags, "r");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_MemWrite) {
|
||||
str8_list_pushf(scratch.arena, &mem_flags, "w");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_MemExecute) {
|
||||
str8_list_pushf(scratch.arena, &mem_flags, "x");
|
||||
}
|
||||
|
||||
String8List cnt_flags = {0};
|
||||
if (header->flags & COFF_SectionFlag_CntCode) {
|
||||
str8_list_pushf(scratch.arena, &cnt_flags, "c");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_CntInitializedData) {
|
||||
str8_list_pushf(scratch.arena, &cnt_flags, "d");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_CntUninitializedData) {
|
||||
str8_list_pushf(scratch.arena, &cnt_flags, "u");
|
||||
}
|
||||
|
||||
String8List mem_extra_flags = {0};
|
||||
if (header->flags & COFF_SectionFlag_MemShared) {
|
||||
str8_list_pushf(scratch.arena, &mem_flags, "s");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_Mem16Bit) {
|
||||
str8_list_pushf(scratch.arena, &mem_extra_flags, "h");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_MemLocked) {
|
||||
str8_list_pushf(scratch.arena, &mem_extra_flags, "l");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_MemDiscardable) {
|
||||
str8_list_pushf(scratch.arena, &mem_extra_flags, "d");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_MemNotCached) {
|
||||
str8_list_pushf(scratch.arena, &mem_extra_flags, "c");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_MemNotPaged) {
|
||||
str8_list_pushf(scratch.arena, &mem_extra_flags, "p");
|
||||
}
|
||||
|
||||
String8List lnk_flags = {0};
|
||||
if (header->flags & COFF_SectionFlag_LnkRemove) {
|
||||
str8_list_pushf(scratch.arena, &lnk_flags, "r");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_LnkCOMDAT) {
|
||||
str8_list_pushf(scratch.arena, &lnk_flags, "c");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_LnkOther) {
|
||||
str8_list_pushf(scratch.arena, &lnk_flags, "o");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_LnkInfo) {
|
||||
str8_list_pushf(scratch.arena, &lnk_flags, "i");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_LnkNRelocOvfl) {
|
||||
str8_list_pushf(scratch.arena, &lnk_flags, "f");
|
||||
}
|
||||
|
||||
String8List other_flags = {0};
|
||||
if (header->flags & COFF_SectionFlag_TypeNoPad) {
|
||||
str8_list_pushf(scratch.arena, &other_flags, "n");
|
||||
}
|
||||
if (header->flags & COFF_SectionFlag_GpRel) {
|
||||
str8_list_pushf(scratch.arena, &other_flags, "g");
|
||||
}
|
||||
|
||||
String8 mem = str8_list_join(scratch.arena, &mem_flags, 0);
|
||||
String8 cnt = str8_list_join(scratch.arena, &cnt_flags, 0);
|
||||
String8 lnk = str8_list_join(scratch.arena, &lnk_flags, 0);
|
||||
String8 ext = str8_list_join(scratch.arena, &mem_extra_flags, 0);
|
||||
String8 oth = str8_list_join(scratch.arena, &other_flags, 0);
|
||||
|
||||
String8List f = {0};
|
||||
str8_list_push(scratch.arena, &f, mem);
|
||||
str8_list_push(scratch.arena, &f, cnt);
|
||||
str8_list_push(scratch.arena, &f, ext);
|
||||
str8_list_push(scratch.arena, &f, lnk);
|
||||
str8_list_push(scratch.arena, &f, oth);
|
||||
|
||||
flags = str8_list_join(scratch.arena, &f, &(StringJoin){ .sep = str8_lit("-") });
|
||||
|
||||
if (!flags.size) {
|
||||
flags = str8_lit("none");
|
||||
}
|
||||
}
|
||||
|
||||
String8List l = {0};
|
||||
str8_list_pushf(scratch.arena, &l, "%-4x", i+1 );
|
||||
str8_list_pushf(scratch.arena, &l, "%-8S", name );
|
||||
str8_list_pushf(scratch.arena, &l, "%08x", header->vsize );
|
||||
str8_list_pushf(scratch.arena, &l, "%08x", header->voff );
|
||||
str8_list_pushf(scratch.arena, &l, "%08x", header->fsize );
|
||||
str8_list_pushf(scratch.arena, &l, "%08x", header->foff );
|
||||
str8_list_pushf(scratch.arena, &l, "%08x", header->relocs_foff);
|
||||
str8_list_pushf(scratch.arena, &l, "%08x", header->lines_foff );
|
||||
str8_list_pushf(scratch.arena, &l, "%08x", header->reloc_count);
|
||||
str8_list_pushf(scratch.arena, &l, "%08x", header->line_count );
|
||||
str8_list_pushf(scratch.arena, &l, "%-5S", align );
|
||||
str8_list_pushf(scratch.arena, &l, "%-10S", flags );
|
||||
if (symlinks[i].size > 0) {
|
||||
str8_list_pushf(scratch.arena, &l, "%S", symlinks[i]);
|
||||
} else {
|
||||
str8_list_pushf(scratch.arena, &l, "[no symlink]");
|
||||
}
|
||||
|
||||
String8 line = str8_list_join(scratch.arena, &l, &(StringJoin){ .sep = str8_lit(" "), });
|
||||
rd_printf("%S", line);
|
||||
|
||||
if (full_name.size != name.size) {
|
||||
rd_indent();
|
||||
rd_printf("Full Name: %S", full_name);
|
||||
rd_unindent();
|
||||
}
|
||||
}
|
||||
|
||||
rd_newline();
|
||||
rd_printf("Flags:");
|
||||
rd_indent();
|
||||
rd_printf("r = MemRead w = MemWrite x = MemExecute");
|
||||
rd_printf("c = CntCode d = InitializedData u = UninitializedData");
|
||||
rd_printf("s = MemShared h = Mem16bit l = MemLocked d = MemDiscardable c = MemNotCached p = MemNotPaged");
|
||||
rd_printf("r = LnkRemove c = LnkComdat o = LnkOther i = LnkInfo f = LnkNRelocOvfl");
|
||||
rd_printf("g = GpRel n = TypeNoPad");
|
||||
rd_unindent();
|
||||
|
||||
rd_unindent();
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_disasm_sections(Arena *arena,
|
||||
String8List *out,
|
||||
String8 indent,
|
||||
String8 raw_data,
|
||||
COFF_MachineType machine,
|
||||
U64 image_base,
|
||||
B32 is_obj,
|
||||
RD_MarkerArray *section_markers,
|
||||
U64 section_count,
|
||||
COFF_SectionHeader *sections)
|
||||
{
|
||||
if (section_count) {
|
||||
for (U64 sect_idx = 0; sect_idx < section_count; ++sect_idx) {
|
||||
COFF_SectionHeader *sect = sections+sect_idx;
|
||||
if (sect->flags & COFF_SectionFlag_CntCode) {
|
||||
U64 sect_off = is_obj ? sect->foff : sect->voff;
|
||||
U64 sect_size = is_obj ? sect->fsize : sect->vsize;
|
||||
String8 raw_code = str8_substr(raw_data, rng_1u64(sect->foff, sect->foff+sect_size));
|
||||
U64 sect_number = sect_idx+1;
|
||||
RD_MarkerArray markers = section_markers[sect_number];
|
||||
|
||||
rd_printf("# Disassembly [Section No. %#llx]", sect_number);
|
||||
rd_indent();
|
||||
rd_print_disasm(arena, out, indent, arch_from_coff_machine(machine), image_base, sect_off, markers.count, markers.v, raw_code);
|
||||
rd_unindent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_raw_data_sections(Arena *arena,
|
||||
String8List *out,
|
||||
String8 indent,
|
||||
String8 raw_data,
|
||||
B32 is_obj,
|
||||
RD_MarkerArray *section_markers,
|
||||
U64 section_count,
|
||||
COFF_SectionHeader *section_table)
|
||||
{
|
||||
if (section_count) {
|
||||
for (U64 sect_idx = 0; sect_idx < section_count; ++sect_idx) {
|
||||
COFF_SectionHeader *sect = section_table+sect_idx;
|
||||
if (sect->fsize > 0) {
|
||||
U64 sect_size = is_obj ? sect->fsize : sect->vsize;
|
||||
String8 raw_sect = str8_substr(raw_data, rng_1u64(sect->foff, sect->foff+sect_size));
|
||||
RD_MarkerArray markers = section_markers[sect_idx];
|
||||
|
||||
rd_printf("# Raw Data [Section No. %#llx]", (sect_idx+1));
|
||||
rd_indent();
|
||||
rd_print_raw_data(arena, out, indent, 32, markers.count, markers.v, raw_sect);
|
||||
rd_unindent();
|
||||
rd_newline();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_relocs(Arena *arena,
|
||||
String8List *out,
|
||||
String8 indent,
|
||||
String8 raw_data,
|
||||
String8 string_table,
|
||||
COFF_MachineType machine,
|
||||
U64 sect_count,
|
||||
COFF_SectionHeader *sect_headers,
|
||||
COFF_Symbol32Array symbols)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
B32 print_header = 1;
|
||||
|
||||
for (U64 sect_idx = 0; sect_idx < sect_count; ++sect_idx) {
|
||||
COFF_SectionHeader *sect_header = sect_headers+sect_idx;
|
||||
COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(raw_data, sect_header);
|
||||
|
||||
if (reloc_info.count) {
|
||||
if (print_header) {
|
||||
print_header = 0;
|
||||
rd_printf("# Relocations");
|
||||
rd_indent();
|
||||
}
|
||||
|
||||
rd_printf("## Section %llx", sect_idx);
|
||||
rd_indent();
|
||||
|
||||
rd_printf("%-4s %-8s %-16s %-16s %-8s %-7s", "No.", "Offset", "Type", "ApplyTo", "SymIdx", "SymName");
|
||||
|
||||
for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; ++reloc_idx) {
|
||||
COFF_Reloc *reloc = (COFF_Reloc*)(raw_data.str + reloc_info.array_off) + reloc_idx;
|
||||
String8 type = coff_string_from_reloc(machine, reloc->type);
|
||||
U64 apply_size = coff_apply_size_from_reloc(machine, reloc->type);
|
||||
|
||||
U64 apply_foff = sect_header->foff + reloc->apply_off;
|
||||
if (apply_foff + apply_size > raw_data.size) {
|
||||
rd_errorf("out of bounds apply file offset %#llx in relocation %#llx", apply_foff, reloc_idx);
|
||||
break;
|
||||
}
|
||||
|
||||
U64 raw_apply;
|
||||
AssertAlways(apply_size <= sizeof(raw_apply));
|
||||
MemoryCopy(&raw_apply, raw_data.str + apply_foff, apply_size);
|
||||
S64 apply = extend_sign64(raw_apply, apply_size);
|
||||
|
||||
if (reloc->isymbol > symbols.count) {
|
||||
rd_errorf("out of bounds symbol index %u in relocation %#llx", reloc->isymbol, reloc_idx);
|
||||
break;
|
||||
}
|
||||
|
||||
COFF_Symbol32 *symbol = symbols.v+reloc->isymbol;
|
||||
String8 symbol_name = coff_read_symbol_name(string_table, &symbol->name);
|
||||
|
||||
String8List line = {0};
|
||||
str8_list_pushf(scratch.arena, &line, "%-4x", reloc_idx );
|
||||
str8_list_pushf(scratch.arena, &line, "%08x", reloc->apply_off);
|
||||
str8_list_pushf(scratch.arena, &line, "%-16S", type );
|
||||
str8_list_pushf(scratch.arena, &line, "%016x", apply );
|
||||
str8_list_pushf(scratch.arena, &line, "%S", symbol_name );
|
||||
|
||||
String8 l = str8_list_join(scratch.arena, &line, &(StringJoin){.sep=str8_lit(" ")});
|
||||
rd_printf("%S", l);
|
||||
}
|
||||
|
||||
rd_unindent();
|
||||
}
|
||||
}
|
||||
|
||||
if (!print_header) {
|
||||
rd_unindent();
|
||||
}
|
||||
rd_newline();
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_symbol_table(Arena *arena,
|
||||
String8List *out,
|
||||
String8 indent,
|
||||
String8 raw_data,
|
||||
B32 is_big_obj,
|
||||
String8 string_table,
|
||||
COFF_Symbol32Array symbols)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
if (symbols.count) {
|
||||
rd_printf("# Symbol Table");
|
||||
rd_indent();
|
||||
|
||||
rd_printf("%-4s %-8s %-10s %-4s %-4s %-4s %-16s %-20s",
|
||||
"No.", "Value", "SectNum", "Aux", "Msb", "Lsb", "Storage", "Name");
|
||||
|
||||
for (U64 i = 0; i < symbols.count; ++i) {
|
||||
COFF_Symbol32 *symbol = &symbols.v[i];
|
||||
String8 name = coff_read_symbol_name(string_table, &symbol->name);
|
||||
String8 msb = coff_string_from_sym_dtype(symbol->type.u.msb);
|
||||
String8 lsb = coff_string_from_sym_type(symbol->type.u.lsb);
|
||||
String8 storage_class = coff_string_from_sym_storage_class(symbol->storage_class);
|
||||
String8 section_number;
|
||||
switch (symbol->section_number) {
|
||||
case COFF_Symbol_UndefinedSection: section_number = str8_lit("Undef"); break;
|
||||
case COFF_Symbol_AbsSection32: section_number = str8_lit("Abs"); break;
|
||||
case COFF_Symbol_DebugSection32: section_number = str8_lit("Debug"); break;
|
||||
default: section_number = push_str8f(scratch.arena, "%010x", symbol->section_number); break;
|
||||
}
|
||||
|
||||
String8List line = {0};
|
||||
str8_list_pushf(scratch.arena, &line, "%-4x", i );
|
||||
str8_list_pushf(scratch.arena, &line, "%08x", symbol->value );
|
||||
str8_list_pushf(scratch.arena, &line, "%-10S", section_number );
|
||||
str8_list_pushf(scratch.arena, &line, "%-4u", symbol->aux_symbol_count);
|
||||
str8_list_pushf(scratch.arena, &line, "%-4S", msb );
|
||||
str8_list_pushf(scratch.arena, &line, "%-4S", lsb );
|
||||
str8_list_pushf(scratch.arena, &line, "%-16S", storage_class );
|
||||
str8_list_pushf(scratch.arena, &line, "%S", name );
|
||||
|
||||
String8 l = str8_list_join(scratch.arena, &line, &(StringJoin){.sep = str8_lit(" ")});
|
||||
rd_printf("%S", l);
|
||||
|
||||
rd_indent();
|
||||
for (U64 k=i+1, c = i+symbol->aux_symbol_count; k <= c; ++k) {
|
||||
void *raw_aux = &symbols.v[k];
|
||||
switch (symbol->storage_class) {
|
||||
case COFF_SymStorageClass_External: {
|
||||
COFF_SymbolFuncDef *func_def = (COFF_SymbolFuncDef*)&symbols.v[k];
|
||||
rd_printf("Tag Index %#x, Total Size %#x, Line Numbers %#x, Next Function %#x",
|
||||
func_def->tag_index, func_def->total_size, func_def->ptr_to_ln, func_def->ptr_to_next_func);
|
||||
} break;
|
||||
case COFF_SymStorageClass_Function: {
|
||||
COFF_SymbolFunc *func = raw_aux;
|
||||
rd_printf("Ordinal Line Number %#x, Next Function %#x", func->ln, func->ptr_to_next_func);
|
||||
} break;
|
||||
case COFF_SymStorageClass_WeakExternal: {
|
||||
COFF_SymbolWeakExt *weak = raw_aux;
|
||||
String8 type = coff_string_from_weak_ext_type(weak->characteristics);
|
||||
rd_printf("Tag Index %#x, Characteristics %S", weak->tag_index, type);
|
||||
} break;
|
||||
case COFF_SymStorageClass_File: {
|
||||
COFF_SymbolFile *file = raw_aux;
|
||||
String8 name = str8_cstring_capped(file->name, file->name+sizeof(file->name));
|
||||
rd_printf("Name %S", name);
|
||||
} break;
|
||||
case COFF_SymStorageClass_Static: {
|
||||
COFF_SymbolSecDef *sd = raw_aux;
|
||||
String8 selection = coff_string_from_comdat_select_type(sd->selection);
|
||||
U32 number = sd->number_lo;
|
||||
if (is_big_obj) {
|
||||
number |= (U32)sd->number_hi << 16;
|
||||
}
|
||||
if (number) {
|
||||
rd_printf("Length %x, Reloc Count %u, Line Count %u, Checksum %x, Section %x, Selection %S",
|
||||
sd->length, sd->number_of_relocations, sd->number_of_ln, sd->check_sum, number, selection);
|
||||
} else {
|
||||
rd_printf("Length %x, Reloc Count %u, Line Count %u, Checksum %x",
|
||||
sd->length, sd->number_of_relocations, sd->number_of_ln, sd->check_sum);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
rd_printf("???");
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
i += symbol->aux_symbol_count;
|
||||
rd_unindent();
|
||||
}
|
||||
|
||||
rd_unindent();
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_big_obj_header(Arena *arena, String8List *out, String8 indent, COFF_BigObjHeader *header)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
String8 time_stamp = coff_string_from_time_stamp(scratch.arena, header->time_stamp);
|
||||
String8 machine = coff_string_from_machine_type(header->machine);
|
||||
|
||||
rd_printf("# Big Obj");
|
||||
rd_indent();
|
||||
rd_printf("Time Stamp : %#x (%S)", header->time_stamp, time_stamp);
|
||||
rd_printf("Machine : %#x (%S)", header->machine, machine );
|
||||
rd_printf("Section Count: %u", header->section_count );
|
||||
rd_printf("Symbol Table : %#x", header->symbol_table_foff);
|
||||
rd_printf("Symbol Count : %u", header->symbol_count );
|
||||
rd_unindent();
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_file_header(Arena *arena, String8List *out, String8 indent, COFF_FileHeader *header)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
String8 time_stamp = coff_string_from_time_stamp(scratch.arena, header->time_stamp);
|
||||
String8 machine = coff_string_from_machine_type(header->machine);
|
||||
String8 flags = coff_string_from_flags(scratch.arena, header->flags);
|
||||
|
||||
rd_printf("# COFF File Header");
|
||||
rd_indent();
|
||||
rd_printf("Time Stamp : %#x (%S)", header->time_stamp, time_stamp );
|
||||
rd_printf("Machine : %#x %S", header->machine, machine );
|
||||
rd_printf("Section Count : %u", header->section_count );
|
||||
rd_printf("Symbol Table : %#x", header->symbol_table_foff );
|
||||
rd_printf("Symbol Count : %u", header->symbol_count );
|
||||
rd_printf("Optional Header Size: %#x (%m)", header->optional_header_size, header->optional_header_size);
|
||||
rd_printf("Flags : %#x (%S)", header->flags, flags );
|
||||
rd_unindent();
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_import(Arena *arena, String8List *out, String8 indent, COFF_ParsedArchiveImportHeader *header)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
String8 machine = coff_string_from_machine_type(header->machine);
|
||||
String8 time_stamp = coff_string_from_time_stamp(scratch.arena, header->time_stamp);
|
||||
|
||||
rd_printf("# Import");
|
||||
rd_indent();
|
||||
rd_printf("Version : %u", header->version );
|
||||
rd_printf("Machine : %S", machine );
|
||||
rd_printf("Time Stamp: %#x (%S)", header->time_stamp, time_stamp );
|
||||
rd_printf("Data Size : %#x (%m)", header->data_size, header->data_size);
|
||||
rd_printf("Hint : %u", header->hint_or_ordinal);
|
||||
rd_printf("Type : %u", header->type );
|
||||
rd_printf("Import By : %u", header->import_by );
|
||||
rd_printf("Function : %S", header->func_name );
|
||||
rd_printf("DLL : %S", header->dll_name );
|
||||
rd_unindent();
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_big_obj(Arena *arena, String8List *out, String8 indent, String8 raw_data, RD_Option opts)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
COFF_FileHeaderInfo header_info = coff_file_header_info_from_data(raw_data);
|
||||
|
||||
String8 raw_header = str8_substr(raw_data, header_info.header_range);
|
||||
String8 raw_section_table = str8_substr(raw_data, header_info.section_table_range);
|
||||
String8 raw_string_table = str8_substr(raw_data, header_info.string_table_range);
|
||||
|
||||
COFF_BigObjHeader *big_obj = (COFF_BigObjHeader *)raw_header.str;
|
||||
COFF_SectionHeader *section_table = (COFF_SectionHeader *)raw_section_table.str;
|
||||
COFF_Symbol32Array symbol_table = coff_symbol_array_from_data_32(scratch.arena, raw_data, header_info.symbol_table_range.min, big_obj->symbol_count);
|
||||
|
||||
if (opts & RD_Option_Headers) {
|
||||
coff_print_big_obj_header(arena, out, indent, big_obj);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Sections) {
|
||||
Rng1U64 sect_headers_range = rng_1u64(sizeof(*big_obj), sizeof(*big_obj) + sizeof(COFF_SectionHeader)*big_obj->section_count);
|
||||
Rng1U64 symbols_range = rng_1u64(big_obj->symbol_table_foff, big_obj->symbol_table_foff + sizeof(COFF_Symbol32)*big_obj->symbol_count);
|
||||
|
||||
if (sect_headers_range.max > raw_data.size) {
|
||||
rd_errorf("not enough bytes to read big obj section headers");
|
||||
goto exit;
|
||||
}
|
||||
if (big_obj->symbol_count) {
|
||||
if (symbols_range.max > raw_data.size) {
|
||||
rd_errorf("not enough bytes to read big obj symbol table");
|
||||
goto exit;
|
||||
}
|
||||
if (contains_1u64(symbols_range, sect_headers_range.min) ||
|
||||
contains_1u64(symbols_range, sect_headers_range.max)) {
|
||||
rd_errorf("section headers and symbol table ranges overlap");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
coff_print_section_table(arena, out, indent, raw_string_table, symbol_table, big_obj->section_count, section_table);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Relocs) {
|
||||
coff_print_relocs(arena, out, indent, raw_data, raw_string_table, big_obj->machine, big_obj->section_count, section_table, symbol_table);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Symbols) {
|
||||
coff_print_symbol_table(arena, out, indent, raw_data, 1, raw_string_table, symbol_table);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
exit:;
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_obj(Arena *arena, String8List *out, String8 indent, String8 raw_data, RD_Option opts)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
COFF_FileHeaderInfo header_info = coff_file_header_info_from_data(raw_data);
|
||||
|
||||
String8 raw_header = str8_substr(raw_data, header_info.header_range);
|
||||
String8 raw_section_table = str8_substr(raw_data, header_info.section_table_range);
|
||||
String8 raw_string_table = str8_substr(raw_data, header_info.string_table_range);
|
||||
|
||||
COFF_FileHeader *header = (COFF_FileHeader *)raw_header.str;
|
||||
COFF_SectionHeader *section_table = (COFF_SectionHeader *)raw_section_table.str;
|
||||
COFF_Symbol32Array symbol_table = coff_symbol_array_from_data_16(scratch.arena, raw_data, header_info.symbol_table_range.min, header->symbol_count);
|
||||
Arch arch = arch_from_coff_machine(header->machine);
|
||||
|
||||
if (opts & RD_Option_Headers) {
|
||||
coff_print_file_header(arena, out, indent, header);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Sections) {
|
||||
Rng1U64 sect_headers_range = rng_1u64(sizeof(*header), sizeof(*header) + sizeof(COFF_SectionHeader)*header->section_count);
|
||||
Rng1U64 symbols_range = rng_1u64(header->symbol_table_foff, header->symbol_table_foff + sizeof(COFF_Symbol16)*header->symbol_count);
|
||||
|
||||
if (sect_headers_range.max > raw_data.size) {
|
||||
rd_errorf("not enough bytes to read obj section headers");
|
||||
goto exit;
|
||||
}
|
||||
if (header->symbol_count) {
|
||||
if (symbols_range.max > raw_data.size) {
|
||||
rd_errorf("not enough bytes to read obj symbol table");
|
||||
goto exit;
|
||||
}
|
||||
if (contains_1u64(symbols_range, sect_headers_range.min) ||
|
||||
contains_1u64(symbols_range, sect_headers_range.max)) {
|
||||
rd_errorf("section headers and symbol table ranges overlap");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
coff_print_section_table(arena, out, indent, raw_string_table, symbol_table, header->section_count, section_table);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Relocs) {
|
||||
coff_print_relocs(arena, out, indent, raw_data, raw_string_table, header->machine, header->section_count, section_table, symbol_table);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Symbols) {
|
||||
coff_print_symbol_table(arena, out, indent, raw_data, 0, raw_string_table, symbol_table);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
RD_MarkerArray *section_markers = 0;
|
||||
if (opts & (RD_Option_Disasm|RD_Option_Rawdata)) {
|
||||
section_markers = rd_section_markers_from_coff_symbol_table(scratch.arena, raw_string_table, header->section_count, symbol_table);
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Rawdata) {
|
||||
coff_raw_data_sections(arena, out, indent, raw_data, 1, section_markers, header->section_count, section_table);
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Disasm) {
|
||||
coff_disasm_sections(arena, out, indent, raw_data, header->machine, 0, 1, section_markers, header->section_count, section_table);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Codeview) {
|
||||
cv_format_debug_sections(arena, out, indent, raw_data, raw_string_table, header->section_count, section_table);
|
||||
}
|
||||
|
||||
if (opts & RD_Option_Dwarf) {
|
||||
DW_Raw dwarf_input = dw_raw_from_coff_section_table(scratch.arena, raw_data, raw_string_table, header->section_count, section_table);
|
||||
dw_format(arena, out, indent, opts, &dwarf_input, arch, ExecutableImageKind_CoffPe);
|
||||
}
|
||||
|
||||
exit:;
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_print_archive(Arena *arena, String8List *out, String8 indent, String8 raw_archive, RD_Option opts)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
COFF_ArchiveParse archive_parse = coff_archive_parse_from_data(raw_archive);
|
||||
|
||||
if (archive_parse.error.size) {
|
||||
rd_errorf("%S", archive_parse.error);
|
||||
return;
|
||||
}
|
||||
|
||||
COFF_ArchiveFirstMember first_member = archive_parse.first_member;
|
||||
{
|
||||
rd_printf("# First Header");
|
||||
rd_indent();
|
||||
|
||||
rd_printf("Symbol Count : %u", first_member.symbol_count);
|
||||
rd_printf("String Table Size: %#llx (%M)", first_member.string_table.size, first_member.string_table.size);
|
||||
|
||||
rd_printf("Members:");
|
||||
rd_indent();
|
||||
|
||||
String8List string_table = str8_split_by_string_chars(scratch.arena, first_member.string_table, str8_lit("\0"), 0);
|
||||
|
||||
if (string_table.node_count == first_member.member_offset_count) {
|
||||
String8Node *string_n = string_table.first;
|
||||
|
||||
for (U64 i = 0; i < string_table.node_count; ++i, string_n = string_n->next) {
|
||||
U32 offset = from_be_u32(first_member.member_offsets[i]);
|
||||
rd_printf("[%4u] %#08x %S", i, offset, string_n->string);
|
||||
}
|
||||
} else {
|
||||
rd_errorf("Member offset count (%llu) doesn't match string table count (%llu)", first_member.member_offset_count);
|
||||
}
|
||||
|
||||
rd_unindent();
|
||||
rd_unindent();
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (archive_parse.has_second_header) {
|
||||
COFF_ArchiveSecondMember second_member = archive_parse.second_member;
|
||||
|
||||
rd_printf("# Second Header");
|
||||
rd_indent();
|
||||
|
||||
rd_printf("Member Count : %u", second_member.member_count);
|
||||
rd_printf("Symbol Count : %u", second_member.symbol_count);
|
||||
rd_printf("String Table Size: %#llx (%M)", second_member.string_table.size, second_member.string_table.size);
|
||||
|
||||
String8List string_table = str8_split_by_string_chars(scratch.arena, second_member.string_table, str8_lit("\0"), 0);
|
||||
|
||||
rd_printf("Members:");
|
||||
rd_indent();
|
||||
if (second_member.symbol_index_count == second_member.symbol_count) {
|
||||
String8Node *string_n = string_table.first;
|
||||
for (U64 i = 0; i < second_member.symbol_count; ++i, string_n = string_n->next) {
|
||||
U16 symbol_number = second_member.symbol_indices[i];
|
||||
if (symbol_number > 0 && symbol_number <= second_member.member_offset_count) {
|
||||
U16 symbol_idx = symbol_number - 1;
|
||||
U32 member_offset = second_member.member_offsets[i];
|
||||
rd_printf("[%4u] %#08x %S", i, member_offset, string_n->string);
|
||||
} else {
|
||||
rd_errorf("[%4u] Out of bounds symbol number %u", i, symbol_number);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rd_errorf("Symbol index count %u doesn't match symbol count %u",
|
||||
second_member.symbol_index_count, second_member.symbol_count);
|
||||
}
|
||||
rd_unindent();
|
||||
|
||||
rd_unindent();
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
if (archive_parse.has_long_names && opts & RD_Option_LongNames) {
|
||||
rd_printf("# Long Names");
|
||||
rd_indent();
|
||||
|
||||
String8List long_names = str8_split_by_string_chars(scratch.arena, archive_parse.long_names, str8_lit("\0"), 0);
|
||||
U64 name_idx = 0;
|
||||
for (String8Node *name_n = long_names.first; name_n != 0; name_n = name_n->next, ++name_idx) {
|
||||
U64 offset = (U64)(name_n->string.str - archive_parse.long_names.str);
|
||||
rd_printf("[%-4u] %#08x %S", name_idx, offset, name_n->string);
|
||||
}
|
||||
|
||||
rd_unindent();
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
U64 member_offset_count = 0;
|
||||
U32 *member_offsets = 0;
|
||||
if (archive_parse.has_second_header) {
|
||||
member_offset_count = archive_parse.second_member.member_offset_count;
|
||||
member_offsets = archive_parse.second_member.member_offsets;
|
||||
} else {
|
||||
HashTable *ht = hash_table_init(scratch.arena, 0x1000);
|
||||
for (U64 i = 0; i < archive_parse.first_member.member_offset_count; ++i) {
|
||||
U32 member_offset = from_be_u32(archive_parse.first_member.member_offsets[i]);
|
||||
if (!hash_table_search_u32(ht, member_offset)) {
|
||||
hash_table_push_u32_raw(scratch.arena, ht, member_offset, 0);
|
||||
}
|
||||
}
|
||||
member_offset_count = ht->count;
|
||||
member_offsets = keys_from_hash_table_u32(scratch.arena, ht);
|
||||
radsort(member_offsets, member_offset_count, u32_is_before);
|
||||
}
|
||||
|
||||
rd_printf("# Members");
|
||||
rd_indent();
|
||||
|
||||
for (U64 i = 0; i < member_offset_count; ++i) {
|
||||
U64 next_member_offset = i+1 < member_offset_count ? member_offsets[i+1] : raw_archive.size;
|
||||
U64 member_offset = member_offsets[i];
|
||||
String8 raw_member = str8_substr(raw_archive, rng_1u64(member_offset, next_member_offset));
|
||||
COFF_ArchiveMember member = coff_archive_member_from_data(raw_member);
|
||||
COFF_DataType member_type = coff_data_type_from_data(member.data);
|
||||
|
||||
rd_printf("Member @ %#llx", member_offset);
|
||||
rd_indent();
|
||||
|
||||
if (opts & RD_Option_Headers) {
|
||||
coff_print_archive_member_header(arena, out, indent, member.header, archive_parse.long_names);
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
switch (member_type) {
|
||||
case COFF_DataType_Obj: {
|
||||
coff_print_obj(arena, out, indent, member.data, opts);
|
||||
} break;
|
||||
case COFF_DataType_BigObj: {
|
||||
coff_print_big_obj(arena, out, indent, member.data, opts);
|
||||
} break;
|
||||
case COFF_DataType_Import: {
|
||||
if (opts & RD_Option_Headers) {
|
||||
COFF_ParsedArchiveImportHeader header = {0};
|
||||
U64 parse_size = coff_parse_import(member.data, 0, &header);
|
||||
if (parse_size) {
|
||||
coff_print_import(arena, out, indent, &header);
|
||||
} else {
|
||||
rd_errorf("not enough bytes to parse import header");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case COFF_DataType_Null: {
|
||||
rd_errorf("unknown member format", member_offset);
|
||||
} break;
|
||||
}
|
||||
|
||||
rd_unindent();
|
||||
rd_newline();
|
||||
}
|
||||
|
||||
rd_unindent();
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
#endif
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef COFF_DUMP_H
|
||||
#define COFF_DUMP_H
|
||||
|
||||
#if 0
|
||||
internal void coff_print_archive_member_header(Arena *arena, String8List *out, String8 indent, COFF_ParsedArchiveMemberHeader header, String8 long_names);
|
||||
internal void coff_print_section_table (Arena *arena, String8List *out, String8 indent, String8 string_table, COFF_Symbol32Array symbols, U64 sect_count, COFF_SectionHeader *sect_headers);
|
||||
internal void coff_disasm_sections (Arena *arena, String8List *out, String8 indent, String8 raw_data, COFF_MachineType machine, U64 image_base, B32 is_obj, RD_MarkerArray *section_markers, U64 section_count, COFF_SectionHeader *sections);
|
||||
internal void coff_raw_data_sections (Arena *arena, String8List *out, String8 indent, String8 raw_data, B32 is_obj, RD_MarkerArray *section_markers, U64 section_count, COFF_SectionHeader *sections);
|
||||
internal void coff_print_relocs (Arena *arena, String8List *out, String8 indent, String8 raw_data, String8 string_table, COFF_MachineType machine, U64 sect_count, COFF_SectionHeader *sect_headers, COFF_Symbol32Array symbols);
|
||||
internal void coff_print_symbol_table (Arena *arena, String8List *out, String8 indent, String8 raw_data, B32 is_big_obj, String8 string_table, COFF_Symbol32Array symbols);
|
||||
internal void coff_print_big_obj_header (Arena *arena, String8List *out, String8 indent, COFF_BigObjHeader *header);
|
||||
internal void coff_print_file_header (Arena *arena, String8List *out, String8 indent, COFF_FileHeader *header);
|
||||
internal void coff_print_import (Arena *arena, String8List *out, String8 indent, COFF_ParsedArchiveImportHeader *header);
|
||||
internal void coff_print_big_obj (Arena *arena, String8List *out, String8 indent, String8 raw_data, RD_Option opts);
|
||||
internal void coff_print_obj (Arena *arena, String8List *out, String8 indent, String8 raw_data, RD_Option opts);
|
||||
internal void coff_print_archive (Arena *arena, String8List *out, String8 indent, String8 raw_archive, RD_Option opts);
|
||||
#endif
|
||||
|
||||
#endif // COFF_DUMP_H
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user