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 +0,0 @@
|
||||
*.sh text=auto eol=lf
|
||||
@@ -1,52 +1,30 @@
|
||||
name: builds
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
build-windows-2022:
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- raddbg
|
||||
- radlink
|
||||
- radbin
|
||||
compiler:
|
||||
- msvc
|
||||
- clang
|
||||
mode:
|
||||
- debug
|
||||
- release
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: build (vs 2022)
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
call build "${{ matrix.target }}" "${{ matrix.compiler }}" "${{ matrix.mode }}" || exit /b 1
|
||||
|
||||
run-torture:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
# - name: Install ASAN
|
||||
# shell: cmd
|
||||
# run: |
|
||||
# "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" modify --installPath "C:\Program Files\Microsoft Visual Studio\2022\Enterprise" --quiet --force --norestart --add Microsoft.VisualStudio.Component.VC.ASAN
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: run-torture
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
call build radlink asan debug no_meta || exit /b 1
|
||||
call build torture debug no_meta || exit /b 1
|
||||
cd build
|
||||
torture -l:radlink || exit /b 1
|
||||
name: builds
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
build-windows-2022:
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: build (vs 2022)
|
||||
shell: cmd
|
||||
run: |
|
||||
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
-4
@@ -1,4 +1,3 @@
|
||||
/build/
|
||||
/local/
|
||||
*~
|
||||
.vscode/settings.json
|
||||
/build/
|
||||
/local/
|
||||
*~
|
||||
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
# 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/EpicGamesExt/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/EpicGamesExt/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,172 +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
|
||||
:: - `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
|
||||
if "%~1"=="release" if "%~2"=="" 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 "%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 "%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
|
||||
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_linker=
|
||||
set clang_common= -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 -mcx16 -msha
|
||||
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 clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /pdbaltpath:%%%%_PDB%%%% -Xlinker /NATVIS:"%~dp0\src\natvis\base.natvis" -Xlinker /opt:ref -Xlinker /opt:icf
|
||||
set clang_out= -o
|
||||
set clang_linker= -Xlinker
|
||||
|
||||
:: --- Per-Build Settings -----------------------------------------------------
|
||||
set link_dll=-DLL
|
||||
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
|
||||
|
||||
:: --- 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\"
|
||||
for /f %%i in ('call git rev-parse HEAD') do set compile=%compile% -DBUILD_GIT_HASH_FULL=\"%%i\"
|
||||
|
||||
:: --- Build & Run Metaprogram ------------------------------------------------
|
||||
if "%meta%"=="1" (
|
||||
echo [doing metagen]
|
||||
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" set didbuild=1 && %compile% ..\src\raddbg\raddbg_main.c %compile_link% %link_icon% %out%raddbg.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 "%tester%"=="1" set didbuild=1 && %compile% ..\src\tester\tester_main.c %compile_link% %out%tester.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 "%eval_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\eval_scratch.c %compile_link% %out%eval_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 && %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" set didbuild=1 && %compile% ..\src\mule\mule_module.cpp %compile_link% %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.c %compile_link% %out%torture.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
|
||||
|
||||
:: --- 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 /rad_alt_pch_dir:%~dp0local\lyra_pgo || 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,81 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# --- Unpack Arguments --------------------------------------------------------
|
||||
for arg in "$@"; do declare $arg='1'; done
|
||||
if [ ! -v gcc ]; then clang=1; fi
|
||||
if [ ! -v release ]; then debug=1; fi
|
||||
if [ -v debug ]; then echo "[debug mode]"; fi
|
||||
if [ -v release ]; then echo "[release mode]"; fi
|
||||
if [ -v clang ]; then compiler="${CC:-clang}"; echo "[clang compile]"; fi
|
||||
if [ -v gcc ]; then compiler="${CC:-gcc}"; echo "[gcc compile]"; fi
|
||||
|
||||
# --- Unpack Command Line Build Arguments -------------------------------------
|
||||
auto_compile_flags=''
|
||||
|
||||
# --- Get Current Git Commit Id -----------------------------------------------
|
||||
git_hash=$(git describe --always --dirty)
|
||||
git_hash_full=$(git rev-parse HEAD)
|
||||
|
||||
# --- Compile/Link Line Definitions -------------------------------------------
|
||||
clang_common="-I../src/ -I/usr/include/freetype2/ -I../local/ -g -DBUILD_GIT_HASH=\"$git_hash\" -DBUILD_GIT_HASH_FULL=\"$git_hash_full\" -Wno-unknown-warning-option -fdiagnostics-absolute-paths -Wall -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 -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Wno-for-loop-analysis -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf"
|
||||
clang_debug="$compiler -g -O0 -DBUILD_DEBUG=1 ${clang_common} ${auto_compile_flags}"
|
||||
clang_release="$compiler -g -O2 -DBUILD_DEBUG=0 ${clang_common} ${auto_compile_flags}"
|
||||
clang_link="-lpthread -lm -lrt -ldl"
|
||||
clang_out="-o"
|
||||
gcc_common="-I../src/ -I../local/ -g -DBUILD_GIT_HASH=\"$git_hash\" -DBUILD_GIT_HASH_FULL=\"$git_hash_full\" -Wno-unknown-warning-option -Wall -Wno-missing-braces -Wno-unused-function -Wno-attributes -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-compare-distinct-pointer-types -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf"
|
||||
gcc_debug="$compiler -g -O0 -DBUILD_DEBUG=1 ${gcc_common} ${auto_compile_flags}"
|
||||
gcc_release="$compiler -g -O2 -DBUILD_DEBUG=0 ${gcc_common} ${auto_compile_flags}"
|
||||
gcc_link="-lpthread -lm -lrt -ldl"
|
||||
gcc_out="-o"
|
||||
|
||||
# --- Per-Build Settings ------------------------------------------------------
|
||||
link_dll="-fPIC"
|
||||
link_os_gfx="-lX11 -lXext"
|
||||
link_render="-lGL -lEGL"
|
||||
link_font_provider="-lfreetype"
|
||||
|
||||
# --- Choose Compile/Link Lines -----------------------------------------------
|
||||
if [ -v gcc ]; then compile_debug="$gcc_debug"; fi
|
||||
if [ -v gcc ]; then compile_release="$gcc_release"; fi
|
||||
if [ -v gcc ]; then compile_link="$gcc_link"; fi
|
||||
if [ -v gcc ]; then out="$gcc_out"; fi
|
||||
if [ -v clang ]; then compile_debug="$clang_debug"; fi
|
||||
if [ -v clang ]; then compile_release="$clang_release"; fi
|
||||
if [ -v clang ]; then compile_link="$clang_link"; fi
|
||||
if [ -v clang ]; then out="$clang_out"; fi
|
||||
if [ -v debug ]; then compile="$compile_debug"; fi
|
||||
if [ -v release ]; then compile="$compile_release"; fi
|
||||
|
||||
# --- Prep Directories --------------------------------------------------------
|
||||
mkdir -p build
|
||||
mkdir -p local
|
||||
|
||||
# --- Build & Run Metaprogram -------------------------------------------------
|
||||
if [ -v no_meta ]; then echo "[skipping metagen]"; fi
|
||||
if [ ! -v no_meta ]
|
||||
then
|
||||
cd build
|
||||
$compile_debug ../src/metagen/metagen_main.c $compile_link $out metagen
|
||||
./metagen
|
||||
cd ..
|
||||
fi
|
||||
|
||||
# --- Build Everything (@build_targets) ---------------------------------------
|
||||
cd build
|
||||
if [ -v raddbg ]; then didbuild=1 && $compile ../src/raddbg/raddbg_main.c $compile_link $link_os_gfx $link_render $link_font_provider $out raddbg; fi
|
||||
if [ -v radlink ]; then didbuild=1 && $compile ../src/linker/lnk.c $compile_link $out radlink; fi
|
||||
if [ -v rdi_from_pdb ]; then didbuild=1 && $compile ../src/rdi_from_pdb/rdi_from_pdb_main.c $compile_link $out rdi_from_pdb; fi
|
||||
if [ -v rdi_from_dwarf ]; then didbuild=1 && $compile ../src/rdi_from_dwarf/rdi_from_dwarf.c $compile_link $out rdi_from_dwarf; fi
|
||||
if [ -v rdi_dump ]; then didbuild=1 && $compile ../src/rdi_dump/rdi_dump_main.c $compile_link $out rdi_dump; fi
|
||||
if [ -v rdi_breakpad_from_pdb ]; then didbuild=1 && $compile ../src/rdi_breakpad_from_pdb/rdi_breakpad_from_pdb_main.c $compile_link $out rdi_breakpad_from_pdb; fi
|
||||
if [ -v ryan_scratch ]; then didbuild=1 && $compile ../src/scratch/ryan_scratch.c $compile_link $link_os_gfx $link_render $link_font_provider $out ryan_scratch; fi
|
||||
cd ..
|
||||
|
||||
# --- Warn On No Builds -------------------------------------------------------
|
||||
if [ ! -v didbuild ]
|
||||
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 rdi_from_pdb\`."
|
||||
exit 1
|
||||
fi
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+174
-117
@@ -1,117 +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 meta telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin debug telemetry", .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 = "build radbin debug telemetry", .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 = "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,21 +0,0 @@
|
||||
@echo off
|
||||
setlocal
|
||||
cd /D "%~dp0"
|
||||
|
||||
echo --- getting test data folder path ---------------------------------------------
|
||||
if not exist .\local\test_data_path.txt (
|
||||
echo error: You must first store the full path of your test data folder inside of `local/test_data_path.txt`.
|
||||
goto :EOF
|
||||
)
|
||||
set /p test_data_folder=<.\local\test_data_path.txt
|
||||
echo test data path: %test_data_folder%
|
||||
echo:
|
||||
|
||||
echo --- building all testing executables ------------------------------------------
|
||||
call build rdi_from_pdb rdi_dump raddbg radlink tester
|
||||
echo:
|
||||
|
||||
echo --- running tests -------------------------------------------------------------
|
||||
pushd build
|
||||
call tester.exe --test_data:%test_data_folder%
|
||||
popd
|
||||
@@ -1,654 +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 = thread_launch(ac_cancel_thread_entry_point, 0);
|
||||
ac_shared->cancel_thread_mutex = mutex_alloc();
|
||||
mutex_take(ac_shared->cancel_thread_mutex);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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);
|
||||
}
|
||||
}
|
||||
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: 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);
|
||||
}
|
||||
if(is_stale)
|
||||
{
|
||||
B32 got_task = (ins_atomic_u64_eval_cond_assign(&n->working_count, 1, 0) == 0);
|
||||
need_request = got_task;
|
||||
}
|
||||
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 = (os_now_microseconds() >= 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;
|
||||
}
|
||||
node->access_pt.last_time_touched_us = os_now_microseconds();
|
||||
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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
mutex_drop(ac_shared->cancel_thread_mutex);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- 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)
|
||||
{
|
||||
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: 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
|
||||
B32 retry = 0;
|
||||
U64 gen = r->gen;
|
||||
AC_Artifact val = r->create(r->key, r->cancel_signal, &retry, &gen);
|
||||
|
||||
// rjf: retry? -> resubmit request
|
||||
if(retry && lane_idx() == 0)
|
||||
{
|
||||
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(!retry && 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(!retry && 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))
|
||||
{
|
||||
n->last_completed_gen = gen;
|
||||
n->val = val;
|
||||
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
|
||||
B32 retry = 0;
|
||||
U64 gen = r->gen;
|
||||
AC_Artifact val = r->create(r->key, r->cancel_signal, &retry, &gen);
|
||||
|
||||
// rjf: restore wide lane ctx
|
||||
lane_ctx(lane_ctx_restore);
|
||||
|
||||
// rjf: retry? -> resubmit request
|
||||
if(retry)
|
||||
{
|
||||
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(!retry)
|
||||
{
|
||||
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(!retry)
|
||||
{
|
||||
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))
|
||||
{
|
||||
n->last_completed_gen = gen;
|
||||
n->val = val;
|
||||
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: disable cancellation scanning
|
||||
//
|
||||
if(lane_idx() == 0)
|
||||
{
|
||||
mutex_take(ac_shared->cancel_thread_mutex);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cancel Thread
|
||||
|
||||
internal void
|
||||
ac_cancel_thread_entry_point(void *p)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
os_sleep_milliseconds(50);
|
||||
MutexScope(ac_shared->cancel_thread_mutex)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,161 +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 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, B32 *retry_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_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;
|
||||
B32 cancelled;
|
||||
U64 _unused_;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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_Shared AC_Shared;
|
||||
struct AC_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
|
||||
// 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;
|
||||
Mutex cancel_thread_mutex;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global AC_Shared *ac_shared = 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
|
||||
|
||||
internal void ac_async_tick(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cancel Thread
|
||||
|
||||
internal void ac_cancel_thread_entry_point(void *p);
|
||||
|
||||
#endif // ARTIFACT_CACHE_H
|
||||
+302
-262
@@ -1,262 +1,302 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arena Functions
|
||||
|
||||
//- rjf: arena creation/destruction
|
||||
|
||||
internal Arena *
|
||||
arena_alloc_(ArenaParams *params)
|
||||
{
|
||||
// rjf: round up reserve/commit sizes
|
||||
U64 reserve_size = params->reserve_size;
|
||||
U64 commit_size = params->commit_size;
|
||||
if(params->flags & ArenaFlag_LargePages)
|
||||
{
|
||||
reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size);
|
||||
commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size);
|
||||
commit_size = AlignPow2(commit_size, os_get_system_info()->page_size);
|
||||
}
|
||||
|
||||
// rjf: reserve/commit initial block
|
||||
void *base = params->optional_backing_buffer;
|
||||
if(base == 0)
|
||||
{
|
||||
if(params->flags & ArenaFlag_LargePages)
|
||||
{
|
||||
base = os_reserve_large(reserve_size);
|
||||
os_commit_large(base, commit_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
base = os_reserve(reserve_size);
|
||||
os_commit(base, commit_size);
|
||||
}
|
||||
raddbg_annotate_vaddr_range(base, reserve_size, "arena %s:%i", params->allocation_site_file, params->allocation_site_line);
|
||||
}
|
||||
|
||||
// rjf: panic on arena creation failure
|
||||
#if OS_FEATURE_GRAPHICAL
|
||||
if(Unlikely(base == 0))
|
||||
{
|
||||
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
|
||||
os_abort(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// rjf: extract arena header & fill
|
||||
Arena *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;
|
||||
#if ARENA_FREE_LIST
|
||||
arena->free_last = 0;
|
||||
#endif
|
||||
AsanPoisonMemoryRegion(base, commit_size);
|
||||
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
|
||||
return arena;
|
||||
}
|
||||
|
||||
internal void
|
||||
arena_release(Arena *arena)
|
||||
{
|
||||
for(Arena *n = arena->current, *prev = 0; n != 0; n = prev)
|
||||
{
|
||||
prev = n->prev;
|
||||
os_release(n, n->res);
|
||||
}
|
||||
}
|
||||
|
||||
//- 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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: 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: 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)
|
||||
{
|
||||
os_commit_large(cmt_ptr, cmt_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
os_commit(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);
|
||||
if(size_to_zero != 0)
|
||||
{
|
||||
MemoryZero(result, size_to_zero);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: panic on failure
|
||||
#if OS_FEATURE_GRAPHICAL
|
||||
if(Unlikely(result == 0))
|
||||
{
|
||||
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
|
||||
os_abort(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;
|
||||
os_release(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;
|
||||
}
|
||||
|
||||
//- 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
-88
@@ -1,88 +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;
|
||||
};
|
||||
|
||||
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;
|
||||
#if ARENA_FREE_LIST
|
||||
Arena *free_last;
|
||||
#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
-247
@@ -1,247 +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_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 24
|
||||
#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/EpicGamesExt/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
|
||||
// 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
-696
File diff suppressed because it is too large
Load Diff
+782
-1026
File diff suppressed because it is too large
Load Diff
+95
-234
@@ -1,234 +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;
|
||||
|
||||
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(ARTIFACT_CACHE_H) && !defined(AC_INIT_MANUAL)
|
||||
ac_init();
|
||||
#endif
|
||||
#if defined(ASYNC_H) && !defined(ASYNC_INIT_MANUAL)
|
||||
async_init(&cmdline);
|
||||
#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(CTRL_CORE_H) && !defined(CTRL_INIT_MANUAL)
|
||||
ctrl_init();
|
||||
#endif
|
||||
#if defined(OS_GFX_H) && !defined(OS_GFX_INIT_MANUAL)
|
||||
os_gfx_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(DBG_ENGINE_CORE_H) && !defined(D_INIT_MANUAL)
|
||||
d_init();
|
||||
#endif
|
||||
#if defined(RADDBG_CORE_H) && !defined(RD_INIT_MANUAL)
|
||||
rd_init(&cmdline);
|
||||
#endif
|
||||
|
||||
//- rjf: launch async threads
|
||||
Thread *async_threads = 0;
|
||||
U64 lane_broadcast_val = 0;
|
||||
{
|
||||
U64 num_main_threads = 1;
|
||||
#if defined(CTRL_CORE_H)
|
||||
num_main_threads += 1;
|
||||
#endif
|
||||
U64 num_async_threads = os_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]);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: call into entry point
|
||||
entry_point(&cmdline);
|
||||
|
||||
//- rjf: join async threads
|
||||
ins_atomic_u32_inc_eval(&global_async_exit);
|
||||
cond_var_broadcast(async_tick_start_cond_var);
|
||||
for EachIndex(idx, async_threads_count)
|
||||
{
|
||||
thread_join(async_threads[idx], max_U64);
|
||||
}
|
||||
|
||||
//- 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, os_now_microseconds()+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(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,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
-22
@@ -1,22 +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_arena.c"
|
||||
#include "base_math.c"
|
||||
#include "base_strings.c"
|
||||
#include "base_hash.c"
|
||||
#include "base_threads.c"
|
||||
#include "base_thread_context.c"
|
||||
#include "base_command_line.c"
|
||||
#include "base_markup.c"
|
||||
#include "base_meta.c"
|
||||
#include "base_log.c"
|
||||
#include "base_entry_point.c"
|
||||
// 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
-26
@@ -1,26 +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_arena.h"
|
||||
#include "base_math.h"
|
||||
#include "base_strings.h"
|
||||
#include "base_hash.h"
|
||||
#include "base_threads.h"
|
||||
#include "base_thread_context.h"
|
||||
#include "base_command_line.h"
|
||||
#include "base_markup.h"
|
||||
#include "base_meta.h"
|
||||
#include "base_log.h"
|
||||
#include "base_entry_point.h"
|
||||
|
||||
#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
-103
@@ -1,103 +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
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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
-65
@@ -1,65 +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;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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
-21
@@ -1,21 +1,21 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal void
|
||||
set_thread_name(String8 string)
|
||||
{
|
||||
ProfThreadName("%.*s", str8_varg(string));
|
||||
os_set_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);
|
||||
}
|
||||
// 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
-22
@@ -1,22 +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
|
||||
|
||||
internal void set_thread_name(String8 string);
|
||||
internal void set_thread_namef(char *fmt, ...);
|
||||
#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
-830
File diff suppressed because it is too large
Load Diff
+16
-62
@@ -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
|
||||
@@ -327,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
|
||||
{
|
||||
U64 count;
|
||||
Rng1U64Node *first;
|
||||
Rng1U64Node *last;
|
||||
};
|
||||
|
||||
typedef struct Rng1U64Array Rng1U64Array;
|
||||
struct Rng1U64Array
|
||||
{
|
||||
Rng1U64 *v;
|
||||
U64 count;
|
||||
};
|
||||
//~ rjf: List Types
|
||||
|
||||
typedef struct Rng1S64Node Rng1S64Node;
|
||||
struct Rng1S64Node
|
||||
@@ -374,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)))
|
||||
@@ -397,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)))
|
||||
@@ -443,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))
|
||||
@@ -455,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))
|
||||
@@ -467,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))
|
||||
@@ -482,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);
|
||||
@@ -494,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);
|
||||
|
||||
@@ -520,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);
|
||||
|
||||
////////////////////////////////
|
||||
@@ -541,7 +521,6 @@ 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
|
||||
@@ -650,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 rng_1u64_array_bsearch(Rng1U64Array arr, 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,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
|
||||
+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, os_now_microseconds(), 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, os_now_microseconds(), 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
|
||||
|
||||
+1937
-2924
File diff suppressed because it is too large
Load Diff
+376
-455
@@ -1,455 +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 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);
|
||||
internal String16 str16_cstring_capped(void *cstr, void *cap);
|
||||
internal String8 str8_cstring_capped_reverse(void *raw_start, void *raw_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);
|
||||
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_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 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 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 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 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);
|
||||
internal void str8_serial_push_cstr(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)
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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
|
||||
|
||||
+87
-225
@@ -1,225 +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)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
TCTX *tctx = push_array(arena, TCTX, 1);
|
||||
tctx->arenas[0] = arena;
|
||||
tctx->arenas[1] = arena_alloc();
|
||||
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
|
||||
os_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)
|
||||
{
|
||||
os_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, os_now_microseconds());
|
||||
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 <= os_now_microseconds() &&
|
||||
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,102 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Functions
|
||||
|
||||
internal Thread
|
||||
thread_launch(ThreadEntryPointFunctionType *f, void *p)
|
||||
{
|
||||
Thread thread = os_thread_launch(f, p);
|
||||
return thread;
|
||||
}
|
||||
|
||||
internal B32
|
||||
thread_join(Thread thread, U64 endt_us)
|
||||
{
|
||||
B32 result = os_thread_join(thread, endt_us);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
thread_detach(Thread thread)
|
||||
{
|
||||
os_thread_detach(thread);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Synchronization Primitive Functions
|
||||
|
||||
//- rjf: recursive mutexes
|
||||
|
||||
internal Mutex mutex_alloc(void) {return os_mutex_alloc();}
|
||||
internal void mutex_release(Mutex mutex) {os_mutex_release(mutex);}
|
||||
internal void mutex_take(Mutex mutex) {os_mutex_take(mutex);}
|
||||
internal void mutex_drop(Mutex mutex) {os_mutex_drop(mutex);}
|
||||
|
||||
//- rjf: reader/writer mutexes
|
||||
|
||||
internal RWMutex rw_mutex_alloc(void) {return os_rw_mutex_alloc();}
|
||||
internal void rw_mutex_release(RWMutex mutex) {os_rw_mutex_release(mutex);}
|
||||
internal void rw_mutex_take(RWMutex mutex, B32 write_mode) {os_rw_mutex_take(mutex, write_mode);}
|
||||
internal void rw_mutex_drop(RWMutex mutex, B32 write_mode) {os_rw_mutex_drop(mutex, write_mode);}
|
||||
|
||||
//- rjf: condition variables
|
||||
|
||||
internal CondVar cond_var_alloc(void) {return os_cond_var_alloc();}
|
||||
internal void cond_var_release(CondVar cv) {os_cond_var_release(cv);}
|
||||
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us) {return os_cond_var_wait(cv, mutex, endt_us);}
|
||||
internal B32 cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us) {return os_cond_var_wait_rw(cv, mutex_rw, write_mode, endt_us);}
|
||||
internal void cond_var_signal(CondVar cv) {os_cond_var_signal(cv);}
|
||||
internal void cond_var_broadcast(CondVar cv) {os_cond_var_broadcast(cv);}
|
||||
|
||||
//- rjf: cross-process semaphores
|
||||
|
||||
internal Semaphore semaphore_alloc(U32 initial_count, U32 max_count, String8 name) {return os_semaphore_alloc(initial_count, max_count, name);}
|
||||
internal void semaphore_release(Semaphore semaphore) {os_semaphore_release(semaphore);}
|
||||
internal Semaphore semaphore_open(String8 name) {return os_semaphore_open(name);}
|
||||
internal void semaphore_close(Semaphore semaphore) {os_semaphore_close(semaphore);}
|
||||
internal B32 semaphore_take(Semaphore semaphore, U64 endt_us) {return os_semaphore_take(semaphore, endt_us);}
|
||||
internal void semaphore_drop(Semaphore semaphore) {os_semaphore_drop(semaphore);}
|
||||
|
||||
//- rjf: barriers
|
||||
|
||||
internal Barrier barrier_alloc(U64 count) {return os_barrier_alloc(count);}
|
||||
internal void barrier_release(Barrier barrier) {os_barrier_release(barrier);}
|
||||
internal void barrier_wait(Barrier barrier) {os_barrier_wait(barrier);}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Table Stripe Functions
|
||||
|
||||
internal StripeArray
|
||||
stripe_array_alloc(Arena *arena)
|
||||
{
|
||||
StripeArray array = {0};
|
||||
array.count = os_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;
|
||||
}
|
||||
@@ -1,133 +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: 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: 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);
|
||||
|
||||
//- 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: 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);
|
||||
|
||||
#endif // BASE_THREADS_H
|
||||
+507
-1112
File diff suppressed because it is too large
Load Diff
+2938
-3021
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,305 +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;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
//- 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);
|
||||
|
||||
#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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1119
-954
File diff suppressed because it is too large
Load Diff
+727
-502
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_Input dwarf_input = dw_input_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 @@
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#include "coff/coff.c"
|
||||
#include "coff/coff_parse.c"
|
||||
#include "coff/coff_dump.c"
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef COFF_INC_H
|
||||
#define COFF_INC_H
|
||||
|
||||
#include "coff/coff.h"
|
||||
#include "coff/coff_parse.h"
|
||||
#include "coff/coff_dump.h"
|
||||
|
||||
#endif // COFF_INC_H
|
||||
@@ -1,387 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal COFF_LibWriterSymbolNode *
|
||||
coff_lib_writer_symbol_list_push(Arena *arena, COFF_LibWriterSymbolList *list, COFF_LibWriterSymbol symbol)
|
||||
{
|
||||
COFF_LibWriterSymbolNode *node = push_array_no_zero(arena, COFF_LibWriterSymbolNode, 1);
|
||||
node->next = 0;
|
||||
node->data = symbol;
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->count += 1;
|
||||
return node;
|
||||
}
|
||||
|
||||
internal COFF_LibWriterMemberNode *
|
||||
coff_lib_writer_member_list_push(Arena *arena, COFF_LibWriterMemberList *list, COFF_LibWriterMember member)
|
||||
{
|
||||
COFF_LibWriterMemberNode *node = push_array_no_zero(arena, COFF_LibWriterMemberNode, 1);
|
||||
node->next = 0;
|
||||
node->data = member;
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->count += 1;
|
||||
return node;
|
||||
}
|
||||
|
||||
internal COFF_LibWriterSymbol *
|
||||
coff_lib_writer_symbol_array_from_list(Arena *arena, COFF_LibWriterSymbolList list)
|
||||
{
|
||||
COFF_LibWriterSymbol *arr = push_array_no_zero(arena, COFF_LibWriterSymbol, list.count + 2);
|
||||
COFF_LibWriterSymbol *ptr = arr + 1;
|
||||
for (COFF_LibWriterSymbolNode *i = list.first; i != 0; i = i->next, ptr += 1) {
|
||||
*ptr = i->data;
|
||||
}
|
||||
MemoryZeroStruct(&arr[0]);
|
||||
MemoryZeroStruct(&arr[list.count+1]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
internal COFF_LibWriterMember *
|
||||
coff_lib_writer_member_array_from_list(Arena *arena, COFF_LibWriterMemberList list)
|
||||
{
|
||||
COFF_LibWriterMember *arr = push_array_no_zero(arena, COFF_LibWriterMember, list.count);
|
||||
COFF_LibWriterMember *ptr = arr;
|
||||
for (COFF_LibWriterMemberNode *i = list.first; i != 0; i = i->next, ptr += 1) {
|
||||
ptr->name = push_str8_copy(arena, i->data.name);
|
||||
ptr->data = push_str8_copy(arena, i->data.data);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
internal int
|
||||
coff_lib_writer_symbol_name_compar(const void *raw_a, const void *raw_b)
|
||||
{
|
||||
const COFF_LibWriterSymbol *sa = raw_a;
|
||||
const COFF_LibWriterSymbol *sb = raw_b;
|
||||
return str8_compar_case_sensitive(&sa->name, &sb->name);
|
||||
}
|
||||
|
||||
internal int
|
||||
coff_lib_writer_symbol_is_before_name(void *raw_a, void *raw_b)
|
||||
{
|
||||
return coff_lib_writer_symbol_name_compar(raw_a, raw_b) < 0;
|
||||
}
|
||||
|
||||
internal int
|
||||
coff_lib_writer_symbol_is_before_member_idx(void *raw_a, void *raw_b)
|
||||
{
|
||||
COFF_LibWriterSymbol *a = raw_a, *b = raw_b;
|
||||
return a->member_idx < b->member_idx;
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_lib_writer_symbol_array_sort_on_name(COFF_LibWriterSymbol *arr, U64 count)
|
||||
{
|
||||
Assert(count >= 2);
|
||||
radsort(arr + 1, count - 2, coff_lib_writer_symbol_is_before_name);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_lib_writer_symbol_array_sort_on_member_idx(COFF_LibWriterSymbol *arr, U64 count)
|
||||
{
|
||||
Assert(count >= 2);
|
||||
radsort(arr + 1, count - 2, coff_lib_writer_symbol_is_before_member_idx);
|
||||
}
|
||||
|
||||
internal COFF_LibWriter *
|
||||
coff_lib_writer_alloc(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
COFF_LibWriter *writer = push_array(arena, COFF_LibWriter, 1);
|
||||
writer->arena = arena;
|
||||
return writer;
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_lib_writer_release(COFF_LibWriter **writer_ptr)
|
||||
{
|
||||
arena_release((*writer_ptr)->arena);
|
||||
*writer_ptr = 0;
|
||||
}
|
||||
|
||||
internal U64
|
||||
coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_data)
|
||||
{
|
||||
U64 member_idx = writer->member_list.count;
|
||||
|
||||
// push obj member
|
||||
COFF_LibWriterMember member = { .name = obj_path, .data = obj_data };
|
||||
COFF_LibWriterMemberNode *member_node = coff_lib_writer_member_list_push(writer->arena, &writer->member_list, member);
|
||||
|
||||
// push external symbols
|
||||
COFF_FileHeaderInfo obj_header = coff_file_header_info_from_data(obj_data);
|
||||
String8 string_table = str8_substr(obj_data, obj_header.string_table_range);
|
||||
String8 symbol_table = str8_substr(obj_data, obj_header.symbol_table_range);
|
||||
COFF_ParsedSymbol symbol;
|
||||
for (U64 symbol_idx = 0; symbol_idx < obj_header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) {
|
||||
symbol = coff_parse_symbol(obj_header, string_table, symbol_table, symbol_idx);
|
||||
COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol);
|
||||
if (interp == COFF_SymbolValueInterp_Regular || interp == COFF_SymbolValueInterp_Common || interp == COFF_SymbolValueInterp_Abs) {
|
||||
if (symbol.storage_class == COFF_SymStorageClass_External) {
|
||||
COFF_LibWriterSymbol lib_symbol = { .name = symbol.name, .member_idx = member_idx };
|
||||
coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, lib_symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return member_idx;
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_lib_writer_push_import(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportByType import_by, String8 name, U16 hint_or_ordinal, COFF_ImportType import_type)
|
||||
{
|
||||
U64 member_idx = lib_writer->member_list.count;
|
||||
|
||||
String8 member_data = coff_make_import_header(lib_writer->arena, machine, time_stamp, dll_name, import_by, name, hint_or_ordinal, import_type);
|
||||
COFF_LibWriterMember member = { .name = dll_name, .data = member_data };
|
||||
COFF_LibWriterMemberNode *member_node = coff_lib_writer_member_list_push(lib_writer->arena, &lib_writer->member_list, member);
|
||||
|
||||
if (name.size) {
|
||||
switch (import_type) {
|
||||
case COFF_ImportHeader_Code: {
|
||||
COFF_LibWriterSymbol thunk_symbol = {0};
|
||||
thunk_symbol.name = push_str8_copy(lib_writer->arena, name);
|
||||
thunk_symbol.member_idx = member_idx;
|
||||
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, thunk_symbol);
|
||||
|
||||
COFF_LibWriterSymbol imp_symbol = {0};
|
||||
imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name);
|
||||
imp_symbol.member_idx = member_idx;
|
||||
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, imp_symbol);
|
||||
} break;
|
||||
case COFF_ImportHeader_Data: {
|
||||
COFF_LibWriterSymbol imp_symbol = {0};
|
||||
imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name);
|
||||
imp_symbol.member_idx = member_idx;
|
||||
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, imp_symbol);
|
||||
} break;
|
||||
case COFF_ImportHeader_Const: { NotImplemented; } break;
|
||||
default: { InvalidPath; } break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
// member lists -> arrays
|
||||
U64 member_count = lib_writer->member_list.count;
|
||||
COFF_LibWriterMember *member_array = coff_lib_writer_member_array_from_list(scratch.arena, lib_writer->member_list);
|
||||
|
||||
// serialize members
|
||||
U64 *member_offsets = push_array_no_zero(scratch.arena, U64, member_count);
|
||||
String8List long_names_list = {0};
|
||||
String8List member_data_list = {0};
|
||||
{
|
||||
HashTable *name_ht = hash_table_init(scratch.arena, 1024);
|
||||
for EachIndex(member_idx, member_count) {
|
||||
COFF_LibWriterMember *member = &member_array[member_idx];
|
||||
|
||||
// make member name
|
||||
String8 name;
|
||||
U64 name_with_slash_size = member->name.size + 1;
|
||||
if (name_with_slash_size > COFF_Archive_MaxShortNameSize) {
|
||||
// have we seen this member name before?
|
||||
KeyValuePair *is_present = hash_table_search_string(name_ht, member->name);
|
||||
if (is_present) {
|
||||
name = is_present->value_string;
|
||||
} else {
|
||||
name = push_str8f(scratch.arena, "/%u", long_names_list.total_size);
|
||||
str8_list_pushf(scratch.arena, &long_names_list, "%S/\n", member->name);
|
||||
hash_table_push_string_string(scratch.arena, name_ht, member->name, name);
|
||||
}
|
||||
} else {
|
||||
name = push_str8f(scratch.arena, "%S/", member->name);
|
||||
}
|
||||
|
||||
member_offsets[member_idx] = member_data_list.total_size;
|
||||
|
||||
String8 member_data = member->data;
|
||||
String8 member_header = coff_make_lib_member_header(scratch.arena, name, time_stamp, 0, 0, mode, member_data.size);
|
||||
|
||||
str8_list_push(scratch.arena, &member_data_list, member_header);
|
||||
str8_list_push(scratch.arena, &member_data_list, member_data);
|
||||
{
|
||||
U64 pad_size = AlignPadPow2(member_data_list.total_size, COFF_Archive_MemberAlign);
|
||||
U8 *pad = push_array(scratch.arena, U8, pad_size);
|
||||
str8_list_push(scratch.arena, &member_data_list, str8(pad, pad_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write long names member
|
||||
if (long_names_list.total_size) {
|
||||
String8 header = coff_make_lib_member_header(scratch.arena, str8_lit("//"), time_stamp, 0, 0, mode, long_names_list.total_size);
|
||||
String8 data = str8_list_join(scratch.arena, &long_names_list, 0);
|
||||
U64 member_offset = member_data_list.total_size + data.size + header.size;
|
||||
{
|
||||
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
|
||||
U8 *pad = push_array(scratch.arena, U8, pad_size);
|
||||
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
|
||||
}
|
||||
str8_list_push_front(scratch.arena, &member_data_list, data);
|
||||
str8_list_push_front(scratch.arena, &member_data_list, header);
|
||||
}
|
||||
|
||||
// compute size for symbol string table
|
||||
U32 name_buffer_size = 0;
|
||||
for (COFF_LibWriterSymbolNode *node = lib_writer->symbol_list.first; node != 0; node = node->next) {
|
||||
name_buffer_size += node->data.name.size;
|
||||
name_buffer_size += 1; // null
|
||||
}
|
||||
|
||||
// compute members base offset
|
||||
U64 members_base_offset;
|
||||
{
|
||||
U64 sizeof_first_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * lib_writer->symbol_list.count + name_buffer_size;
|
||||
U64 sizeof_second_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * member_count + sizeof(U32) + sizeof(U16) * lib_writer->symbol_list.count + name_buffer_size;
|
||||
U64 sizeof_long_names = sizeof(COFF_ArchiveMemberHeader) + long_names_list.total_size;
|
||||
|
||||
sizeof_first_header = AlignPow2(sizeof_first_header, COFF_Archive_MemberAlign);
|
||||
sizeof_second_header = AlignPow2(sizeof_second_header, COFF_Archive_MemberAlign);
|
||||
sizeof_long_names = AlignPow2(sizeof_long_names, COFF_Archive_MemberAlign);
|
||||
|
||||
members_base_offset = sizeof(g_coff_archive_sig);
|
||||
members_base_offset += sizeof_first_header;
|
||||
if (emit_second_member) {
|
||||
members_base_offset += sizeof_second_header;
|
||||
}
|
||||
if (long_names_list.total_size) {
|
||||
members_base_offset += sizeof_long_names;
|
||||
}
|
||||
}
|
||||
|
||||
// write second linker member
|
||||
if (emit_second_member) {
|
||||
U64 symbols_count;
|
||||
COFF_LibWriterSymbol *symbols;
|
||||
{
|
||||
U64 symbols_count_with_null = lib_writer->symbol_list.count + 2;
|
||||
COFF_LibWriterSymbol *symbols_with_null = coff_lib_writer_symbol_array_from_list(scratch.arena, lib_writer->symbol_list);
|
||||
coff_lib_writer_symbol_array_sort_on_name(symbols_with_null, symbols_count_with_null);
|
||||
|
||||
symbols_count = symbols_count_with_null - 2;
|
||||
symbols = symbols_with_null + 1;
|
||||
}
|
||||
|
||||
// write symbol name buffer
|
||||
U8 *name_buffer = push_array_no_zero(scratch.arena, U8, name_buffer_size);
|
||||
{
|
||||
U64 name_cursor = 0;
|
||||
for EachIndex(symbol_idx, symbols_count) {
|
||||
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
|
||||
MemoryCopy(name_buffer + name_cursor, symbol->name.str, symbol->name.size);
|
||||
name_buffer[name_cursor + symbol->name.size] = '\0';
|
||||
name_cursor += symbol->name.size + 1;
|
||||
}
|
||||
}
|
||||
|
||||
U32 member_count32 = safe_cast_u32(member_count);
|
||||
U32 symbol_count32 = safe_cast_u32(symbols_count);
|
||||
|
||||
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, member_count);
|
||||
U16 *member_idx16_arr = push_array_no_zero(scratch.arena, U16, symbols_count);
|
||||
|
||||
// write member offset array
|
||||
for EachIndex(member_idx, member_count) {
|
||||
U64 member_offset = members_base_offset + member_offsets[member_idx];
|
||||
U32 member_off32 = safe_cast_u32(member_offset);
|
||||
member_off32_arr[member_idx] = member_off32;
|
||||
}
|
||||
|
||||
// write member offset indices for each symbol
|
||||
for EachIndex(symbol_idx, symbols_count) {
|
||||
// member offset indices are 1-based
|
||||
U64 member_idx = symbols[symbol_idx].member_idx + 1;
|
||||
U16 member_idx16 = safe_cast_u16(member_idx);
|
||||
member_idx16_arr[symbol_idx] = member_idx16;
|
||||
}
|
||||
|
||||
// layout second member data
|
||||
String8List second_member_data_list = {0};
|
||||
str8_list_push(scratch.arena, &second_member_data_list, str8_struct(&member_count32));
|
||||
str8_list_push(scratch.arena, &second_member_data_list, str8_array(member_off32_arr, member_count));
|
||||
str8_list_push(scratch.arena, &second_member_data_list, str8_struct(&symbol_count32));
|
||||
str8_list_push(scratch.arena, &second_member_data_list, str8_array(member_idx16_arr, symbols_count));
|
||||
str8_list_push(scratch.arena, &second_member_data_list, str8(name_buffer, name_buffer_size));
|
||||
|
||||
String8 member_data = str8_list_join(scratch.arena, &second_member_data_list, 0);
|
||||
String8 member_header = coff_make_lib_member_header(scratch.arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
|
||||
|
||||
U64 member_offset = member_data_list.total_size + member_data.size + member_header.size;
|
||||
{
|
||||
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
|
||||
U8 *pad = push_array(scratch.arena, U8, pad_size);
|
||||
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
|
||||
}
|
||||
str8_list_push_front(scratch.arena, &member_data_list, member_data);
|
||||
str8_list_push_front(scratch.arena, &member_data_list, member_header);
|
||||
}
|
||||
|
||||
// write first linker member (obsolete, but kept for compatability reasons)
|
||||
{
|
||||
U64 symbols_count;
|
||||
COFF_LibWriterSymbol *symbols;
|
||||
{
|
||||
U64 symbols_count_with_null = lib_writer->symbol_list.count + 2;
|
||||
COFF_LibWriterSymbol *symbols_with_null = coff_lib_writer_symbol_array_from_list(scratch.arena, lib_writer->symbol_list);
|
||||
coff_lib_writer_symbol_array_sort_on_member_idx(symbols_with_null, symbols_count_with_null);
|
||||
|
||||
symbols_count = symbols_count_with_null - 2;
|
||||
symbols = symbols_with_null + 1;
|
||||
}
|
||||
|
||||
// write symbol name buffer
|
||||
U8 *name_buffer = push_array_no_zero(scratch.arena, U8, name_buffer_size);
|
||||
{
|
||||
U64 name_cursor = 0;
|
||||
for EachIndex(symbol_idx, symbols_count) {
|
||||
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
|
||||
MemoryCopy(name_buffer + name_cursor, symbol->name.str, symbol->name.size);
|
||||
name_buffer[name_cursor + symbol->name.size] = '\0';
|
||||
name_cursor += symbol->name.size + 1;
|
||||
}
|
||||
}
|
||||
|
||||
U32 symbol_count_be = from_be_u32(symbols_count);
|
||||
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, symbols_count);
|
||||
|
||||
for EachIndex(symbol_idx, symbols_count) {
|
||||
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
|
||||
|
||||
// write big endian member offset
|
||||
U64 member_offset = members_base_offset + member_offsets[symbol->member_idx];
|
||||
U32 member_off32 = from_be_u32(safe_cast_u32(member_offset));
|
||||
member_off32_arr[symbol_idx] = member_off32;
|
||||
}
|
||||
|
||||
// layout first member data
|
||||
String8List first_member_data_list = {0};
|
||||
str8_list_push(scratch.arena, &first_member_data_list, str8_struct(&symbol_count_be));
|
||||
str8_list_push(scratch.arena, &first_member_data_list, str8_array(member_off32_arr, symbols_count));
|
||||
str8_list_push(scratch.arena, &first_member_data_list, str8(name_buffer, name_buffer_size));
|
||||
|
||||
String8 member_data = str8_list_join(scratch.arena, &first_member_data_list, 0);
|
||||
String8 member_header = coff_make_lib_member_header(scratch.arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
|
||||
|
||||
U64 member_offset = sizeof(g_coff_archive_sig) + member_header.size + member_data.size;
|
||||
{
|
||||
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
|
||||
U8 *pad = push_array(scratch.arena, U8, pad_size);
|
||||
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
|
||||
}
|
||||
str8_list_push_front(scratch.arena, &member_data_list, member_data);
|
||||
str8_list_push_front(scratch.arena, &member_data_list, member_header);
|
||||
}
|
||||
|
||||
// archive signature
|
||||
str8_list_push_front(scratch.arena, &member_data_list, str8_struct(&g_coff_archive_sig));
|
||||
|
||||
String8 raw_lib = str8_list_join(arena, &member_data_list, 0);
|
||||
|
||||
scratch_end(scratch);
|
||||
return raw_lib;
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef COFF_LIB_WRITER_H
|
||||
#define COFF_LIB_WRITER_H
|
||||
|
||||
typedef struct COFF_LibWriterMember
|
||||
{
|
||||
String8 name;
|
||||
String8 data;
|
||||
} COFF_LibWriterMember;
|
||||
|
||||
typedef struct COFF_LibWriterMemberNode
|
||||
{
|
||||
COFF_LibWriterMember data;
|
||||
struct COFF_LibWriterMemberNode *next;
|
||||
} COFF_LibWriterMemberNode;
|
||||
|
||||
typedef struct COFF_LibWriterMemberList
|
||||
{
|
||||
U64 count;
|
||||
COFF_LibWriterMemberNode *first;
|
||||
COFF_LibWriterMemberNode *last;
|
||||
} COFF_LibWriterMemberList;
|
||||
|
||||
typedef struct COFF_LibWriterSymbol
|
||||
{
|
||||
String8 name;
|
||||
U64 member_idx;
|
||||
} COFF_LibWriterSymbol;
|
||||
|
||||
typedef struct COFF_LibWriterSymbolNode
|
||||
{
|
||||
COFF_LibWriterSymbol data;
|
||||
struct COFF_LibWriterSymbolNode *next;
|
||||
} COFF_LibWriterSymbolNode;
|
||||
|
||||
typedef struct COFF_LibWriterSymbolList
|
||||
{
|
||||
U64 count;
|
||||
COFF_LibWriterSymbolNode *first;
|
||||
COFF_LibWriterSymbolNode *last;
|
||||
} COFF_LibWriterSymbolList;
|
||||
|
||||
typedef struct COFF_LibWriter
|
||||
{
|
||||
Arena *arena;
|
||||
COFF_LibWriterMemberList member_list;
|
||||
COFF_LibWriterSymbolList symbol_list;
|
||||
} COFF_LibWriter;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal COFF_LibWriterSymbolNode * coff_lib_writer_symbol_list_push(Arena *arena, COFF_LibWriterSymbolList *list, COFF_LibWriterSymbol symbol);
|
||||
internal COFF_LibWriterMemberNode * coff_lib_writer_member_list_push(Arena *arena, COFF_LibWriterMemberList *list, COFF_LibWriterMember member);
|
||||
|
||||
internal COFF_LibWriterSymbol * coff_lib_writer_symbol_array_from_list(Arena *arena, COFF_LibWriterSymbolList list);
|
||||
internal COFF_LibWriterMember * coff_lib_writer_member_array_from_list(Arena *arena, COFF_LibWriterMemberList list);
|
||||
|
||||
internal void coff_lib_writer_symbol_array_sort_on_name(COFF_LibWriterSymbol *arr, U64 count);
|
||||
internal void coff_lib_writer_symbol_array_sort_on_member_idx(COFF_LibWriterSymbol *arr, U64 count);
|
||||
|
||||
internal COFF_LibWriter * coff_lib_writer_alloc(void);
|
||||
internal void coff_lib_writer_release(COFF_LibWriter **writer_ptr);
|
||||
internal U64 coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_data);
|
||||
internal void coff_lib_writer_push_import(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportByType import_by, String8 name, U16 hint_or_ordinal, COFF_ImportType import_type);
|
||||
internal String8 coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member);
|
||||
|
||||
#endif // COFF_LIB_WRITER_H
|
||||
|
||||
@@ -1,500 +0,0 @@
|
||||
internal COFF_ObjWriter*
|
||||
coff_obj_writer_alloc(COFF_TimeStamp time_stamp, COFF_MachineType machine)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
COFF_ObjWriter *obj_writer = push_array(arena, COFF_ObjWriter, 1);
|
||||
obj_writer->arena = arena;
|
||||
obj_writer->time_stamp = time_stamp;
|
||||
obj_writer->machine = machine;
|
||||
return obj_writer;
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_obj_writer_release(COFF_ObjWriter **obj_writer)
|
||||
{
|
||||
arena_release((*obj_writer)->arena);
|
||||
*obj_writer = 0;
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
String8List srl = {0};
|
||||
|
||||
String8List string_table = {0};
|
||||
U32 *string_table_size = push_array(scratch.arena, U32, 1);
|
||||
*string_table_size = sizeof(*string_table_size);
|
||||
str8_list_push(scratch.arena, &string_table, str8_struct(string_table_size));
|
||||
|
||||
//
|
||||
// assing section numbers
|
||||
//
|
||||
U64 obj_sections_count;
|
||||
COFF_ObjSection **obj_sections;
|
||||
{
|
||||
obj_sections_count = obj_writer->sect_count;
|
||||
obj_sections = push_array(scratch.arena, COFF_ObjSection *, obj_writer->sect_count);
|
||||
U64 sect_idx = 0;
|
||||
for (COFF_ObjSectionNode *sect_n = obj_writer->sect_first; sect_n != 0; sect_n = sect_n->next, sect_idx += 1) {
|
||||
COFF_ObjSection *sect = §_n->v;
|
||||
sect->section_number = sect_idx+1;
|
||||
obj_sections[sect_idx] = sect;
|
||||
|
||||
}
|
||||
}
|
||||
AssertAlways(obj_sections_count <= max_U16);
|
||||
|
||||
//
|
||||
// serialize symbol table
|
||||
//
|
||||
String8List symbol_table = {0};
|
||||
{
|
||||
{
|
||||
U64 symbol_idx = 0;
|
||||
for (COFF_ObjSymbolNode *symbol_n = obj_writer->symbol_first; symbol_n != 0; symbol_n = symbol_n->next) {
|
||||
COFF_ObjSymbol *s = &symbol_n->v;
|
||||
|
||||
// assign symbol index
|
||||
s->idx = symbol_idx++;
|
||||
symbol_idx += s->aux_symbols.node_count;
|
||||
}
|
||||
}
|
||||
|
||||
U64 symbol_idx = 0;
|
||||
for (COFF_ObjSymbolNode *symbol_n = obj_writer->symbol_first; symbol_n != 0; symbol_n = symbol_n->next) {
|
||||
COFF_ObjSymbol *s = &symbol_n->v;
|
||||
|
||||
COFF_Symbol16 *d = push_array(scratch.arena, COFF_Symbol16, 1);
|
||||
str8_list_push(scratch.arena, &symbol_table, str8_struct(d));
|
||||
|
||||
COFF_SymbolName name = {0};
|
||||
// long name
|
||||
if (s->name.size > sizeof(name.short_name)) {
|
||||
U64 string_table_offset = string_table.total_size;
|
||||
str8_list_push(scratch.arena, &string_table, s->name);
|
||||
str8_list_push(scratch.arena, &string_table, str8_lit("\0"));
|
||||
|
||||
name.long_name.zeroes = 0;
|
||||
name.long_name.string_table_offset = safe_cast_u32(string_table_offset);
|
||||
}
|
||||
// short name
|
||||
else {
|
||||
MemoryCopyStr8(name.short_name, s->name);
|
||||
MemoryZeroTyped(name.short_name + s->name.size, sizeof(name.short_name) - s->name.size);
|
||||
}
|
||||
|
||||
// symbol header
|
||||
AssertAlways(s->aux_symbols.node_count <= max_U8);
|
||||
d->name = name;
|
||||
d->value = s->value;
|
||||
switch (s->loc.type) {
|
||||
case COFF_SymbolLocation_Null: break;
|
||||
case COFF_SymbolLocation_Section: d->section_number = safe_cast_u16(s->loc.u.section->section_number); break;
|
||||
case COFF_SymbolLocation_Abs: d->section_number = COFF_Symbol_AbsSection16; break;
|
||||
case COFF_SymbolLocation_Undef: d->section_number = COFF_Symbol_UndefinedSection; break;
|
||||
case COFF_SymbolLocation_Common: d->section_number = COFF_Symbol_UndefinedSection; break;
|
||||
}
|
||||
d->type = s->type;
|
||||
d->storage_class = s->storage_class;
|
||||
d->aux_symbol_count = 0;
|
||||
|
||||
U64 start_symbol_idx = symbol_idx;
|
||||
if (s->storage_class == COFF_SymStorageClass_WeakExternal) {
|
||||
if (s->aux_symbols.node_count > 0) {
|
||||
COFF_ObjSymbolWeak *s_weak = (COFF_ObjSymbolWeak *)s->aux_symbols.first->string.str;
|
||||
COFF_SymbolWeakExt *d_weak = push_array(scratch.arena, COFF_SymbolWeakExt, 1);
|
||||
d_weak->tag_index = s_weak->tag ? s_weak->tag->idx : max_U32;
|
||||
d_weak->characteristics = s_weak->characteristics;
|
||||
|
||||
str8_list_push(scratch.arena, &symbol_table, str8_struct(d_weak));
|
||||
symbol_idx += 1;
|
||||
}
|
||||
} else if (s->storage_class == COFF_SymStorageClass_Static) {
|
||||
if (s->aux_symbols.node_count > 0) {
|
||||
Assert(s->loc.type == COFF_SymbolLocation_Section);
|
||||
COFF_ObjSection *sect = s->loc.u.section;
|
||||
|
||||
COFF_ObjSymbolSecDef *s_sd = (COFF_ObjSymbolSecDef *)s->aux_symbols.first->string.str;
|
||||
COFF_SymbolSecDef *d_sd = push_array(scratch.arena, COFF_SymbolSecDef, 1);
|
||||
|
||||
d_sd->length = safe_cast_u32(sect->data.total_size);
|
||||
d_sd->number_of_relocations = (U16)sect->reloc_count;
|
||||
d_sd->check_sum = 0;
|
||||
d_sd->number_lo = s_sd->selection == COFF_ComdatSelect_Associative ? safe_cast_u16(s_sd->associate->section_number) : 0;
|
||||
d_sd->selection = s_sd->selection;
|
||||
|
||||
str8_list_push(scratch.arena, &symbol_table, str8_struct(d_sd));
|
||||
symbol_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
U8 processed_aux_symbol_count = (U8)(symbol_idx - start_symbol_idx);
|
||||
|
||||
for (U64 aux_idx = processed_aux_symbol_count; aux_idx < s->aux_symbols.node_count; aux_idx += 1) {
|
||||
COFF_Symbol16 *a = push_array(scratch.arena, COFF_Symbol16, 1);
|
||||
str8_list_push(scratch.arena, &symbol_table, str8_struct(a));
|
||||
}
|
||||
|
||||
d->aux_symbol_count = (U8)s->aux_symbols.node_count;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// file header
|
||||
//
|
||||
COFF_FileHeader *file_header = push_array(scratch.arena, COFF_FileHeader, 1);
|
||||
file_header->machine = obj_writer->machine;
|
||||
file_header->section_count = obj_sections_count;
|
||||
file_header->time_stamp = obj_writer->time_stamp;
|
||||
file_header->symbol_table_foff = 0;
|
||||
file_header->symbol_count = safe_cast_u32(symbol_table.node_count);
|
||||
file_header->optional_header_size = 0;
|
||||
file_header->flags = 0;
|
||||
str8_list_push(scratch.arena, &srl, str8_struct(file_header));
|
||||
|
||||
//
|
||||
// section table
|
||||
//
|
||||
|
||||
COFF_SectionHeader *sectab = push_array(scratch.arena, COFF_SectionHeader, obj_sections_count);
|
||||
str8_list_push(scratch.arena, &srl, str8_array(sectab, obj_sections_count));
|
||||
{
|
||||
for (U64 sect_idx = 0; sect_idx < obj_sections_count; sect_idx += 1) {
|
||||
COFF_ObjSection *s = obj_sections[sect_idx];
|
||||
COFF_SectionHeader *d = §ab[sect_idx];
|
||||
|
||||
// section name
|
||||
String8 sect_name = s->name;
|
||||
if (sect_name.size > sizeof(d->name)) {
|
||||
U64 sect_name_off = string_table.total_size;
|
||||
str8_list_push(scratch.arena, &string_table, push_cstr(scratch.arena, sect_name));
|
||||
|
||||
sect_name = push_str8f(scratch.arena, "/%u", sect_name_off);
|
||||
AssertAlways(sect_name.size <= sizeof(d->name));
|
||||
}
|
||||
|
||||
// alloc zero nodes
|
||||
for (String8Node *data_n = s->data.first; data_n != 0; data_n = data_n->next) {
|
||||
if (data_n->string.str == 0 && data_n->string.size > 0) {
|
||||
data_n->string = str8(push_array(scratch.arena, U8, data_n->string.size), data_n->string.size);
|
||||
}
|
||||
}
|
||||
|
||||
// section data
|
||||
U64 data_foff = 0;
|
||||
U64 data_size = 0;
|
||||
if (s->data.total_size > 0) {
|
||||
data_foff = srl.total_size;
|
||||
data_size = s->data.total_size;
|
||||
str8_list_concat_in_place(&srl, &s->data);
|
||||
}
|
||||
|
||||
// section relocs
|
||||
U64 relocs_foff = 0;
|
||||
if (s->reloc_count) {
|
||||
AssertAlways(s->reloc_count <= max_U16);
|
||||
COFF_Reloc *relocs = push_array(scratch.arena, COFF_Reloc, s->reloc_count);
|
||||
U64 reloc_idx = 0;
|
||||
for (COFF_ObjRelocNode *reloc_n = s->reloc_first; reloc_n != 0; reloc_n = reloc_n->next, reloc_idx += 1) {
|
||||
COFF_ObjReloc *rs = &reloc_n->v;
|
||||
COFF_Reloc *rd = &relocs[reloc_idx];
|
||||
rd->apply_off = rs->apply_off;
|
||||
rd->isymbol = rs->symbol->idx;
|
||||
rd->type = rs->type;
|
||||
}
|
||||
relocs_foff = srl.total_size;
|
||||
str8_list_push(scratch.arena, &srl, str8_array(relocs, s->reloc_count));
|
||||
}
|
||||
|
||||
// section header
|
||||
MemoryCopyStr8(d->name, sect_name);
|
||||
MemoryZeroTyped(d->name + sect_name.size, sizeof(d->name) - sect_name.size);
|
||||
d->vsize = 0;
|
||||
d->voff = 0;
|
||||
d->fsize = data_size;
|
||||
d->foff = data_foff;
|
||||
d->relocs_foff = relocs_foff;
|
||||
d->lines_foff = 0;
|
||||
d->reloc_count = safe_cast_u32(s->reloc_count);
|
||||
d->line_count = 0;
|
||||
d->flags = s->flags;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// symbol table
|
||||
//
|
||||
if (symbol_table.total_size || string_table.total_size > sizeof(*string_table_size)) {
|
||||
file_header->symbol_table_foff = srl.total_size;
|
||||
str8_list_concat_in_place(&srl, &symbol_table);
|
||||
}
|
||||
|
||||
//
|
||||
// string table
|
||||
//
|
||||
if (string_table.total_size) {
|
||||
*string_table_size = safe_cast_u32(string_table.total_size);
|
||||
str8_list_concat_in_place(&srl, &string_table);
|
||||
}
|
||||
|
||||
//
|
||||
// join
|
||||
//
|
||||
String8 obj = str8_list_join(arena, &srl, 0);
|
||||
|
||||
scratch_end(scratch);
|
||||
return obj;
|
||||
}
|
||||
|
||||
internal COFF_ObjSection *
|
||||
coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data)
|
||||
{
|
||||
COFF_ObjSectionNode *sect_n = push_array(obj_writer->arena, COFF_ObjSectionNode, 1);
|
||||
SLLQueuePush(obj_writer->sect_first, obj_writer->sect_last, sect_n);
|
||||
obj_writer->sect_count += 1;
|
||||
|
||||
COFF_ObjSection *sect = §_n->v;
|
||||
sect->name = name;
|
||||
sect->flags = flags;
|
||||
|
||||
if (data.size) {
|
||||
str8_list_push(obj_writer->arena, §->data, data);
|
||||
}
|
||||
|
||||
return sect;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymbolLocation loc, COFF_SymbolType type, COFF_SymStorageClass storage_class)
|
||||
{
|
||||
COFF_ObjSymbolNode *n = push_array(obj_writer->arena, COFF_ObjSymbolNode, 1);
|
||||
SLLQueuePush(obj_writer->symbol_first, obj_writer->symbol_last, n);
|
||||
obj_writer->symbol_count += 1;
|
||||
|
||||
COFF_ObjSymbol *s = &n->v;
|
||||
s->name = name;
|
||||
s->value = value;
|
||||
s->loc = loc;
|
||||
s->type = type;
|
||||
s->storage_class = storage_class;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_extern(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section)
|
||||
{
|
||||
COFF_SymbolLocation loc = {0};
|
||||
loc.type = COFF_SymbolLocation_Section;
|
||||
loc.u.section = section;
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, value, loc, (COFF_SymbolType){0}, COFF_SymStorageClass_External);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_extern_func(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section)
|
||||
{
|
||||
COFF_SymbolType type = { .u.msb = COFF_SymDType_Func };
|
||||
COFF_SymbolLocation loc = { .type = COFF_SymbolLocation_Section, .u.section = section };
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, value, loc, type, COFF_SymStorageClass_External);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_static(COFF_ObjWriter *obj_writer, String8 name, U32 off, COFF_ObjSection *section)
|
||||
{
|
||||
COFF_SymbolLocation loc = {0};
|
||||
loc.type = COFF_SymbolLocation_Section;
|
||||
loc.u.section = section;
|
||||
|
||||
COFF_SymbolType symtype = {0};
|
||||
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, off, loc, symtype, COFF_SymStorageClass_Static);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_secdef(COFF_ObjWriter *obj_writer, COFF_ObjSection *section, COFF_ComdatSelectType selection)
|
||||
{
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol_static(obj_writer, section->name, 0, section);
|
||||
COFF_ObjSymbolSecDef *sd = push_array(obj_writer->arena, COFF_ObjSymbolSecDef, 1);
|
||||
sd->selection = selection;
|
||||
str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(sd));
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_associative(COFF_ObjWriter *obj_writer, COFF_ObjSection *head, COFF_ObjSection *associate)
|
||||
{
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol_static(obj_writer, head->name, 0, head);
|
||||
COFF_ObjSymbolSecDef *sd = push_array(obj_writer->arena, COFF_ObjSymbolSecDef, 1);
|
||||
sd->selection = COFF_ComdatSelect_Associative;
|
||||
sd->associate = associate;
|
||||
str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(sd));
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_weak(COFF_ObjWriter *obj_writer, String8 name, COFF_WeakExtType characteristics, COFF_ObjSymbol *tag)
|
||||
{
|
||||
COFF_SymbolLocation loc = {0};
|
||||
COFF_SymbolType symtype = {0};
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, COFF_Symbol_UndefinedSection, loc, symtype, COFF_SymStorageClass_WeakExternal);
|
||||
|
||||
COFF_ObjSymbolWeak *weak_ext = push_array(obj_writer->arena, COFF_ObjSymbolWeak, 1);
|
||||
weak_ext->tag = tag;
|
||||
weak_ext->characteristics = characteristics;
|
||||
|
||||
str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(weak_ext));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_abs(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymStorageClass storage_class)
|
||||
{
|
||||
COFF_SymbolLocation loc = {0};
|
||||
loc.type = COFF_SymbolLocation_Abs;
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, value, loc, (COFF_SymbolType){0}, storage_class);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_undef(COFF_ObjWriter *obj_writer, String8 name)
|
||||
{
|
||||
COFF_SymbolType type = {0};
|
||||
COFF_SymbolLocation loc = {0};
|
||||
loc.type = COFF_SymbolLocation_Undef;
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, 0, loc, type, COFF_SymStorageClass_External);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_undef_func(COFF_ObjWriter *obj_writer, String8 name)
|
||||
{
|
||||
COFF_SymbolType type = {0};
|
||||
type.u.msb = COFF_SymDType_Func;
|
||||
COFF_SymbolLocation loc = {0};
|
||||
loc.type = COFF_SymbolLocation_Undef;
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, 0, loc, type, COFF_SymStorageClass_External);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_undef_sect(COFF_ObjWriter *obj_writer, String8 name, U32 value)
|
||||
{
|
||||
COFF_SymbolType type = {0};
|
||||
COFF_SymbolLocation loc = {0};
|
||||
loc.type = COFF_SymbolLocation_Undef;
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, value, loc, type, COFF_SymStorageClass_Section);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_sect(COFF_ObjWriter *obj_writer, String8 name, COFF_ObjSection *sect)
|
||||
{
|
||||
COFF_SymbolType type = {0};
|
||||
COFF_SymbolLocation loc = {0};
|
||||
loc.type = COFF_SymbolLocation_Section;
|
||||
loc.u.section = sect;
|
||||
|
||||
// strip align flags
|
||||
COFF_SectionFlags expected_flags = sect->flags & ~(COFF_SectionFlag_AlignMask << COFF_SectionFlag_AlignShift);
|
||||
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, expected_flags, loc, type, COFF_SymStorageClass_Section);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal COFF_ObjSymbol *
|
||||
coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32 size)
|
||||
{
|
||||
COFF_SymbolType type = {0};
|
||||
COFF_SymbolLocation loc = {0};
|
||||
loc.type = COFF_SymbolLocation_Common;
|
||||
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, size, loc, type, COFF_SymStorageClass_External);
|
||||
return s;
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_obj_writer_set_default_symbol(COFF_ObjSymbol *weak_symbol, COFF_ObjSymbol *default_symbol)
|
||||
{
|
||||
AssertAlways(weak_symbol->storage_class == COFF_SymStorageClass_WeakExternal);
|
||||
COFF_ObjSymbolWeak *w = (COFF_ObjSymbolWeak *)weak_symbol->aux_symbols.first->string.str;
|
||||
w->tag = default_symbol;
|
||||
}
|
||||
|
||||
internal COFF_ObjReloc*
|
||||
coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType type)
|
||||
{
|
||||
COFF_ObjRelocNode *reloc_n = push_array(obj_writer->arena, COFF_ObjRelocNode, 1);
|
||||
SLLQueuePush(sect->reloc_first, sect->reloc_last, reloc_n);
|
||||
sect->reloc_count += 1;
|
||||
|
||||
COFF_ObjReloc *reloc = &reloc_n->v;
|
||||
reloc->apply_off = apply_off;
|
||||
reloc->symbol = symbol;
|
||||
reloc->type = type;
|
||||
|
||||
return reloc;
|
||||
}
|
||||
|
||||
internal COFF_ObjReloc *
|
||||
coff_obj_writer_section_push_reloc_addr32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol)
|
||||
{
|
||||
COFF_RelocType reloc_type = 0;
|
||||
switch (obj_writer->machine) {
|
||||
case COFF_MachineType_Unknown: break;
|
||||
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr32; break;
|
||||
default: { NotImplemented; } break;
|
||||
}
|
||||
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
|
||||
}
|
||||
|
||||
internal COFF_ObjReloc *
|
||||
coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol)
|
||||
{
|
||||
COFF_RelocType reloc_type = 0;
|
||||
switch (obj_writer->machine) {
|
||||
case COFF_MachineType_Unknown: break;
|
||||
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr64; break;
|
||||
default: { NotImplemented; } break;
|
||||
}
|
||||
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
|
||||
}
|
||||
|
||||
internal COFF_ObjReloc *
|
||||
coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol)
|
||||
{
|
||||
COFF_RelocType reloc_type = 0;
|
||||
switch (obj_writer->machine) {
|
||||
case COFF_MachineType_Unknown: break;
|
||||
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr32Nb; break;
|
||||
default: { NotImplemented; } break;
|
||||
}
|
||||
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_obj_writer_push_directive(COFF_ObjWriter *obj_writer, String8 directive)
|
||||
{
|
||||
if (obj_writer->drectve_sect == 0) {
|
||||
local_persist const U8 bom_sig[] = { ' ', ' ', ' ' };
|
||||
obj_writer->drectve_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".drectve"), COFF_SectionFlag_LnkInfo|COFF_SectionFlag_LnkRemove|COFF_SectionFlag_Align1Bytes, str8_array_fixed(bom_sig));
|
||||
}
|
||||
String8List *data = &obj_writer->drectve_sect->data;
|
||||
str8_list_push(obj_writer->arena, data, directive);
|
||||
str8_list_pushf(obj_writer->arena, data, " ");
|
||||
}
|
||||
|
||||
internal int
|
||||
coff_obj_section_is_before(void *raw_a, void *raw_b)
|
||||
{
|
||||
COFF_ObjSection **a = raw_a;
|
||||
COFF_ObjSection **b = raw_b;
|
||||
return (*a)->section_number < (*b)->section_number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
#ifndef COFF_OBJ_WRITER_H
|
||||
#define COFF_OBJ_WRITER_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COFF_SymbolLocation_Null,
|
||||
COFF_SymbolLocation_Section,
|
||||
COFF_SymbolLocation_Abs,
|
||||
COFF_SymbolLocation_Undef,
|
||||
COFF_SymbolLocation_Common,
|
||||
} COFF_SymbolLocationType;
|
||||
|
||||
typedef struct COFF_SymbolLocation
|
||||
{
|
||||
COFF_SymbolLocationType type;
|
||||
union {
|
||||
struct COFF_ObjSection *section;
|
||||
} u;
|
||||
} COFF_SymbolLocation;
|
||||
|
||||
typedef struct COFF_ObjSymbolWeak
|
||||
{
|
||||
struct COFF_ObjSymbol *tag;
|
||||
COFF_WeakExtType characteristics;
|
||||
} COFF_ObjSymbolWeak;
|
||||
|
||||
typedef struct COFF_ObjSymbolSecDef
|
||||
{
|
||||
COFF_ComdatSelectType selection;
|
||||
struct COFF_ObjSection *associate;
|
||||
} COFF_ObjSymbolSecDef;
|
||||
|
||||
typedef struct COFF_ObjSymbol
|
||||
{
|
||||
String8 name;
|
||||
U32 value;
|
||||
COFF_SymbolLocation loc;
|
||||
COFF_SymbolType type;
|
||||
COFF_SymStorageClass storage_class;
|
||||
String8List aux_symbols;
|
||||
U32 idx;
|
||||
} COFF_ObjSymbol;
|
||||
|
||||
typedef struct COFF_ObjSymbolNode
|
||||
{
|
||||
struct COFF_ObjSymbolNode *next;
|
||||
COFF_ObjSymbol v;
|
||||
} COFF_ObjSymbolNode;
|
||||
|
||||
typedef struct COFF_ObjReloc
|
||||
{
|
||||
U32 apply_off;
|
||||
COFF_ObjSymbol *symbol;
|
||||
COFF_RelocType type;
|
||||
} COFF_ObjReloc;
|
||||
|
||||
typedef struct COFF_ObjRelocNode
|
||||
{
|
||||
struct COFF_ObjRelocNode *next;
|
||||
COFF_ObjReloc v;
|
||||
} COFF_ObjRelocNode;
|
||||
|
||||
typedef struct COFF_ObjSection
|
||||
{
|
||||
String8 name;
|
||||
String8List data;
|
||||
COFF_SectionFlags flags;
|
||||
|
||||
U64 reloc_count;
|
||||
COFF_ObjRelocNode *reloc_first;
|
||||
COFF_ObjRelocNode *reloc_last;
|
||||
|
||||
U32 section_number;
|
||||
} COFF_ObjSection;
|
||||
|
||||
typedef struct COFF_ObjSectionNode
|
||||
{
|
||||
struct COFF_ObjSectionNode *next;
|
||||
COFF_ObjSection v;
|
||||
} COFF_ObjSectionNode;
|
||||
|
||||
typedef struct COFF_ObjWriter
|
||||
{
|
||||
Arena *arena;
|
||||
COFF_TimeStamp time_stamp;
|
||||
COFF_MachineType machine;
|
||||
U64 symbol_count;
|
||||
COFF_ObjSymbolNode *symbol_first;
|
||||
COFF_ObjSymbolNode *symbol_last;
|
||||
U64 sect_count;
|
||||
COFF_ObjSectionNode *sect_first;
|
||||
COFF_ObjSectionNode *sect_last;
|
||||
COFF_ObjSection *drectve_sect;
|
||||
} COFF_ObjWriter;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal COFF_ObjWriter * coff_obj_writer_alloc(COFF_TimeStamp time_stamp, COFF_MachineType machine);
|
||||
internal void coff_obj_writer_release(COFF_ObjWriter **obj_writer);
|
||||
internal String8 coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer);
|
||||
|
||||
internal COFF_ObjSection * coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data);
|
||||
|
||||
internal COFF_ObjSymbol* coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymbolLocation loc, COFF_SymbolType type, COFF_SymStorageClass storage_class);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_extern_func(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_extern(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_static(COFF_ObjWriter *obj_writer, String8 name, U32 off, COFF_ObjSection *section);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_secdef(COFF_ObjWriter *obj_writer, COFF_ObjSection *section, COFF_ComdatSelectType selection);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_associative(COFF_ObjWriter *obj_writer, COFF_ObjSection *head, COFF_ObjSection *associate);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_weak(COFF_ObjWriter *obj_writer, String8 name, COFF_WeakExtType characteristics, COFF_ObjSymbol *tag);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_abs(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymStorageClass storage_class);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef(COFF_ObjWriter *obj_writer, String8 name);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_func(COFF_ObjWriter *obj_writer, String8 name);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_sect(COFF_ObjWriter *obj_writer, String8 name, U32 value);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_sect(COFF_ObjWriter *obj_writer, String8 name, COFF_ObjSection *sect);
|
||||
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32 size);
|
||||
|
||||
internal void coff_obj_writer_set_default_symbol(COFF_ObjSymbol *weak_symbol, COFF_ObjSymbol *default_symbol);
|
||||
|
||||
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType reloc_type);
|
||||
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
|
||||
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
|
||||
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
|
||||
|
||||
internal void coff_obj_writer_push_directive(COFF_ObjWriter *obj_writer, String8 directive);
|
||||
|
||||
#endif // COFF_OBJ_WRITER_H
|
||||
|
||||
@@ -1,991 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal B32
|
||||
coff_is_big_obj(String8 raw_coff)
|
||||
{
|
||||
B32 is_big_obj = 0;
|
||||
if (raw_coff.size >= sizeof(COFF_BigObjHeader)) {
|
||||
COFF_BigObjHeader *file_header32 = (COFF_BigObjHeader*)(raw_coff.str);
|
||||
is_big_obj = file_header32->sig1 == COFF_MachineType_Unknown &&
|
||||
file_header32->sig2 == max_U16 &&
|
||||
file_header32->version >= 2 &&
|
||||
MemoryCompare(file_header32->magic, g_coff_big_header_magic, sizeof(file_header32->magic)) == 0;
|
||||
}
|
||||
return is_big_obj;
|
||||
}
|
||||
|
||||
internal B32
|
||||
coff_is_obj(String8 raw_coff)
|
||||
{
|
||||
B32 is_obj = 0;
|
||||
|
||||
if (raw_coff.size >= sizeof(COFF_FileHeader)) {
|
||||
COFF_FileHeader *header = (COFF_FileHeader*)(raw_coff.str);
|
||||
|
||||
// validate machine
|
||||
B32 is_machine_type_valid = 0;
|
||||
switch (header->machine) {
|
||||
case COFF_MachineType_Unknown:
|
||||
case COFF_MachineType_X86: case COFF_MachineType_X64:
|
||||
case COFF_MachineType_Am33: case COFF_MachineType_Arm:
|
||||
case COFF_MachineType_Arm64: case COFF_MachineType_ArmNt:
|
||||
case COFF_MachineType_Ebc: case COFF_MachineType_Ia64:
|
||||
case COFF_MachineType_M32R: case COFF_MachineType_Mips16:
|
||||
case COFF_MachineType_MipsFpu:case COFF_MachineType_MipsFpu16:
|
||||
case COFF_MachineType_PowerPc:case COFF_MachineType_PowerPcFp:
|
||||
case COFF_MachineType_R4000: case COFF_MachineType_RiscV32:
|
||||
case COFF_MachineType_RiscV64:case COFF_MachineType_RiscV128:
|
||||
case COFF_MachineType_Sh3: case COFF_MachineType_Sh3Dsp:
|
||||
case COFF_MachineType_Sh4: case COFF_MachineType_Sh5:
|
||||
case COFF_MachineType_Thumb: case COFF_MachineType_WceMipsV2:
|
||||
{
|
||||
is_machine_type_valid = 1;
|
||||
}break;
|
||||
}
|
||||
|
||||
if (is_machine_type_valid) {
|
||||
// validate section count
|
||||
U64 section_count = header->section_count;
|
||||
U64 section_hdr_opl_off = sizeof(*header) + section_count*sizeof(COFF_SectionHeader);
|
||||
if (raw_coff.size >= section_hdr_opl_off) {
|
||||
|
||||
COFF_SectionHeader *section_hdrs = (COFF_SectionHeader*)(raw_coff.str + sizeof(*header));
|
||||
COFF_SectionHeader *section_hdr_opl = section_hdrs + section_count;
|
||||
|
||||
// validate section ranges
|
||||
B32 is_sect_range_valid = 1;
|
||||
for (COFF_SectionHeader *sec_hdr = section_hdrs;
|
||||
sec_hdr < section_hdr_opl;
|
||||
sec_hdr += 1) {
|
||||
if (!(sec_hdr->flags & COFF_SectionFlag_CntUninitializedData)) {
|
||||
U64 min = sec_hdr->foff;
|
||||
U64 max = min + sec_hdr->fsize;
|
||||
if (sec_hdr->fsize > 0 && !(section_hdr_opl_off <= min && min <= max && max <= raw_coff.size)) {
|
||||
is_sect_range_valid = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_sect_range_valid) {
|
||||
// validate symbol table
|
||||
U64 symbol_table_off = header->symbol_table_foff;
|
||||
U64 symbol_table_size = sizeof(COFF_Symbol16)*header->symbol_count;
|
||||
U64 symbol_table_opl_off = symbol_table_off + symbol_table_size;
|
||||
|
||||
// don't validate symbol table when there is none
|
||||
if (symbol_table_off == 0 && symbol_table_size == 0) {
|
||||
symbol_table_off = section_hdr_opl_off;
|
||||
symbol_table_opl_off = section_hdr_opl_off;
|
||||
}
|
||||
|
||||
is_obj = (section_hdr_opl_off <= symbol_table_off &&
|
||||
symbol_table_off <= symbol_table_opl_off &&
|
||||
symbol_table_opl_off <= raw_coff.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return is_obj;
|
||||
}
|
||||
|
||||
internal COFF_FileHeaderInfo
|
||||
coff_file_header_info_from_data(String8 raw_coff)
|
||||
{
|
||||
COFF_FileHeaderInfo info = {0};
|
||||
if (coff_is_big_obj(raw_coff)) {
|
||||
COFF_BigObjHeader *header32 = (COFF_BigObjHeader*)raw_coff.str;
|
||||
info.is_big_obj = 1;
|
||||
info.machine = header32->machine;
|
||||
info.section_count_no_null = header32->section_count;
|
||||
info.symbol_count = header32->symbol_count;
|
||||
info.symbol_size = sizeof(COFF_Symbol32);
|
||||
info.header_range = rng_1u64(0, sizeof(COFF_BigObjHeader));
|
||||
info.section_table_range = rng_1u64(info.header_range.max, info.header_range.max + sizeof(COFF_SectionHeader) * header32->section_count);
|
||||
info.symbol_table_range = rng_1u64(header32->symbol_table_foff, header32->symbol_table_foff + sizeof(COFF_Symbol32) * header32->symbol_count);
|
||||
info.string_table_range = rng_1u64(info.symbol_table_range.max, raw_coff.size);
|
||||
} else if (coff_is_obj(raw_coff)) {
|
||||
COFF_FileHeader *header16 = (COFF_FileHeader*)raw_coff.str;
|
||||
info.is_big_obj = 0;
|
||||
info.machine = header16->machine;
|
||||
info.section_count_no_null = header16->section_count;
|
||||
info.symbol_count = header16->symbol_count;
|
||||
info.symbol_size = sizeof(COFF_Symbol16);
|
||||
info.header_range = rng_1u64(0, sizeof(COFF_FileHeader));
|
||||
info.section_table_range = rng_1u64(info.header_range.max, info.header_range.max + sizeof(COFF_SectionHeader) * header16->section_count);
|
||||
info.symbol_table_range = rng_1u64(header16->symbol_table_foff, header16->symbol_table_foff + sizeof(COFF_Symbol16) * header16->symbol_count);
|
||||
info.string_table_range = rng_1u64(info.symbol_table_range.max, raw_coff.size);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
internal COFF_SectionHeader **
|
||||
coff_section_table_from_data(Arena *arena, String8 data, Rng1U64 section_table_range)
|
||||
{
|
||||
U64 section_count = dim_1u64(section_table_range) / sizeof(COFF_SectionHeader);
|
||||
COFF_SectionHeader **section_table = push_array_no_zero(arena, COFF_SectionHeader *, section_count+1);
|
||||
section_table[0] = push_array(arena, COFF_SectionHeader, 1);
|
||||
for (U64 i = 0; i < section_count; ++i) {
|
||||
section_table[i+1] = str8_deserial_get_raw_ptr(data, section_table_range.min + i*sizeof(COFF_SectionHeader), sizeof(COFF_SectionHeader));
|
||||
}
|
||||
return section_table;
|
||||
}
|
||||
|
||||
internal COFF_ParsedSymbol
|
||||
coff_parse_symbol32(String8 string_table, COFF_Symbol32 *sym32)
|
||||
{
|
||||
COFF_ParsedSymbol result = {0};
|
||||
result.name = coff_read_symbol_name(string_table, &sym32->name);
|
||||
result.value = sym32->value;
|
||||
result.section_number = sym32->section_number;
|
||||
result.type = sym32->type;
|
||||
result.storage_class = sym32->storage_class;
|
||||
result.aux_symbol_count = sym32->aux_symbol_count;
|
||||
result.raw_symbol = sym32;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal COFF_ParsedSymbol
|
||||
coff_parse_symbol16(String8 string_table, COFF_Symbol16 *sym16)
|
||||
{
|
||||
COFF_ParsedSymbol result = {0};
|
||||
result.name = coff_read_symbol_name(string_table, &sym16->name);
|
||||
result.value = sym16->value;
|
||||
if (sym16->section_number == COFF_Symbol_DebugSection16) {
|
||||
result.section_number = COFF_Symbol_DebugSection32;
|
||||
} else if (sym16->section_number == COFF_Symbol_AbsSection16) {
|
||||
result.section_number = COFF_Symbol_AbsSection32;
|
||||
} else {
|
||||
result.section_number = (U32)sym16->section_number;
|
||||
}
|
||||
result.type = sym16->type;
|
||||
result.storage_class = sym16->storage_class;
|
||||
result.aux_symbol_count = sym16->aux_symbol_count;
|
||||
result.raw_symbol = sym16;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal COFF_ParsedSymbol
|
||||
coff_parse_symbol(COFF_FileHeaderInfo header, String8 string_table, String8 symbol_table, U32 symbol_idx)
|
||||
{
|
||||
COFF_ParsedSymbol symbol;
|
||||
if (header.is_big_obj) {
|
||||
symbol = coff_parse_symbol32(string_table, (COFF_Symbol32 *)symbol_table.str + symbol_idx);
|
||||
} else {
|
||||
symbol = coff_parse_symbol16(string_table, (COFF_Symbol16 *)symbol_table.str + symbol_idx);
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
internal COFF_Symbol32Array
|
||||
coff_symbol_array_from_data_16(Arena *arena, String8 raw_coff, U64 symbol_array_off, U64 symbol_count)
|
||||
{
|
||||
COFF_Symbol32Array result = {0};
|
||||
result.count = symbol_count;
|
||||
result.v = push_array_no_zero_aligned(arena, COFF_Symbol32, result.count, 8);
|
||||
|
||||
Rng1U64 sym16_arr_range = rng_1u64(symbol_array_off, symbol_array_off + sizeof(COFF_Symbol16) * symbol_count);
|
||||
String8 raw_sym16_arr = str8_substr(raw_coff, sym16_arr_range);
|
||||
COFF_Symbol16 *sym16_arr = (COFF_Symbol16 *)raw_sym16_arr.str;
|
||||
|
||||
for (U64 isymbol = 0, count = raw_sym16_arr.size / sizeof(COFF_Symbol16); isymbol < count; isymbol += 1) {
|
||||
COFF_Symbol16 *sym16 = &sym16_arr[isymbol];
|
||||
COFF_Symbol32 *sym32 = &result.v[isymbol];
|
||||
|
||||
sym32->name = sym16->name;
|
||||
sym32->value = sym16->value;
|
||||
if (sym16->section_number == COFF_Symbol_DebugSection16) {
|
||||
sym32->section_number = COFF_Symbol_DebugSection32;
|
||||
} else if (sym16->section_number == COFF_Symbol_AbsSection16) {
|
||||
sym32->section_number = COFF_Symbol_AbsSection32;
|
||||
} else {
|
||||
sym32->section_number = (U32)sym16->section_number;
|
||||
}
|
||||
sym32->type.v = sym16->type.v;
|
||||
sym32->storage_class = sym16->storage_class;
|
||||
sym32->aux_symbol_count = sym16->aux_symbol_count;
|
||||
|
||||
// copy aux symbols
|
||||
for (U64 iaux = isymbol+1, iaux_hi = Min(count, iaux+sym16->aux_symbol_count); iaux < iaux_hi; iaux += 1) {
|
||||
COFF_Symbol16 *aux16 = sym16_arr + iaux;
|
||||
COFF_Symbol32 *aux32 = result.v + iaux;
|
||||
|
||||
// 32bit COFF uses 16bit aux symbols
|
||||
MemoryCopy(aux32, aux16, sizeof(COFF_Symbol16));
|
||||
MemoryZero((U8 *)aux32 + sizeof(COFF_Symbol16), sizeof(COFF_Symbol32)-sizeof(COFF_Symbol16));
|
||||
}
|
||||
|
||||
// take into account aux symbols
|
||||
isymbol += sym32->aux_symbol_count;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal COFF_Symbol32Array
|
||||
coff_symbol_array_from_data_32(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count)
|
||||
{
|
||||
COFF_Symbol32Array result;
|
||||
result.count = symbol_count;
|
||||
result.v = (COFF_Symbol32 *)(data.str + symbol_array_off);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal COFF_Symbol32Array
|
||||
coff_symbol_array_from_data(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count, U64 symbol_size)
|
||||
{
|
||||
COFF_Symbol32Array result = {0};
|
||||
switch (symbol_size) {
|
||||
case sizeof(COFF_Symbol16): result = coff_symbol_array_from_data_16(arena, data, symbol_array_off, symbol_count); break;
|
||||
case sizeof(COFF_Symbol32): result = coff_symbol_array_from_data_32(arena, data, symbol_array_off, symbol_count); break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal COFF_Symbol16Node *
|
||||
coff_symbol16_list_push(Arena *arena, COFF_Symbol16List *list, COFF_Symbol16 symbol)
|
||||
{
|
||||
COFF_Symbol16Node *node = push_array(arena, COFF_Symbol16Node, 1);
|
||||
node->next = 0;
|
||||
node->data = symbol;
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->count += 1;
|
||||
return node;
|
||||
}
|
||||
|
||||
internal COFF_SymbolValueInterpType
|
||||
coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_class)
|
||||
{
|
||||
if (storage_class == COFF_SymStorageClass_Section && section_number == COFF_Symbol_UndefinedSection) {
|
||||
return COFF_SymbolValueInterp_Undefined;
|
||||
}
|
||||
if (storage_class == COFF_SymStorageClass_External && value == 0 && section_number == COFF_Symbol_UndefinedSection) {
|
||||
return COFF_SymbolValueInterp_Undefined;
|
||||
}
|
||||
if (storage_class == COFF_SymStorageClass_External && value != 0 && section_number == COFF_Symbol_UndefinedSection) {
|
||||
return COFF_SymbolValueInterp_Common;
|
||||
}
|
||||
if (section_number == COFF_Symbol_AbsSection32) {
|
||||
return COFF_SymbolValueInterp_Abs;
|
||||
}
|
||||
if (section_number == COFF_Symbol_DebugSection32) {
|
||||
return COFF_SymbolValueInterp_Debug;
|
||||
}
|
||||
if (storage_class == COFF_SymStorageClass_WeakExternal) {
|
||||
return COFF_SymbolValueInterp_Weak;
|
||||
}
|
||||
return COFF_SymbolValueInterp_Regular;
|
||||
}
|
||||
|
||||
internal COFF_SymbolValueInterpType
|
||||
coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol)
|
||||
{
|
||||
return coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
|
||||
}
|
||||
|
||||
internal B32
|
||||
coff_is_undefined_data_symbol(COFF_ParsedSymbol symbol)
|
||||
{
|
||||
COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol);
|
||||
return interp == COFF_SymbolValueInterp_Undefined && symbol.storage_class == COFF_SymStorageClass_External;
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out)
|
||||
{
|
||||
Assert(coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class) == COFF_SymbolValueInterp_Regular);
|
||||
Assert(symbol.aux_symbol_count > 0);
|
||||
|
||||
if (is_big_obj) {
|
||||
COFF_SymbolSecDef *sd = (COFF_SymbolSecDef *)((COFF_Symbol32 *)symbol.raw_symbol + 1);
|
||||
if (selection_out) *selection_out = sd->selection;
|
||||
if (length_out) *length_out = sd->length;
|
||||
if (check_sum_out) *check_sum_out = sd->check_sum;
|
||||
if (number_out) *number_out = Compose32Bit(sd->number_hi, sd->number_lo);
|
||||
} else {
|
||||
COFF_SymbolSecDef *sd = (COFF_SymbolSecDef *)((COFF_Symbol16 *)symbol.raw_symbol + 1);
|
||||
if (selection_out) *selection_out = sd->selection;
|
||||
if (length_out) *length_out = sd->length;
|
||||
if (check_sum_out) *check_sum_out = sd->check_sum;
|
||||
if (number_out) *number_out = sd->number_lo;
|
||||
}
|
||||
}
|
||||
|
||||
internal COFF_SymbolWeakExt *
|
||||
coff_parse_weak_tag(COFF_ParsedSymbol symbol, B32 is_big_obj)
|
||||
{
|
||||
Assert(coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class) == COFF_SymbolValueInterp_Weak);
|
||||
Assert(symbol.aux_symbol_count > 0);
|
||||
|
||||
void *tag;
|
||||
if (is_big_obj) {
|
||||
tag = (COFF_SymbolWeakExt *)((COFF_Symbol32 *)symbol.raw_symbol + 1);
|
||||
} else {
|
||||
tag = (COFF_SymbolWeakExt *)((COFF_Symbol16 *)symbol.raw_symbol + 1);
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
internal COFF_RelocNode *
|
||||
coff_reloc_list_push(Arena *arena, COFF_RelocList *list, COFF_Reloc reloc)
|
||||
{
|
||||
COFF_RelocNode *node = push_array(arena, COFF_RelocNode, 1);
|
||||
node->data = reloc;
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
++list->count;
|
||||
return node;
|
||||
}
|
||||
|
||||
internal COFF_RelocInfo
|
||||
coff_reloc_info_from_section_header(String8 data, COFF_SectionHeader *header)
|
||||
{
|
||||
COFF_RelocInfo result = {0};
|
||||
if (header->flags & COFF_SectionFlag_LnkNRelocOvfl && header->reloc_count == max_U16) {
|
||||
COFF_Reloc counter;
|
||||
U64 read_size = str8_deserial_read_struct(data, header->relocs_foff, &counter);
|
||||
if (read_size == sizeof(counter) && counter.apply_off > 0) {
|
||||
result.array_off = header->relocs_foff + sizeof(COFF_Reloc);
|
||||
result.count = counter.apply_off - 1; // exclude counter entry
|
||||
}
|
||||
} else {
|
||||
result.array_off = header->relocs_foff;
|
||||
result.count = header->reloc_count;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_resource_string_from_str16(Arena *arena, String16 string)
|
||||
{
|
||||
AssertAlways(string.size <= max_U16);
|
||||
U16 size16 = (U16)string.size;
|
||||
|
||||
U16 *buffer = push_array_no_zero(arena, U16, size16 + 1);
|
||||
MemoryCopy(buffer + 0, &size16, sizeof(size16));
|
||||
MemoryCopy(buffer + 1, string.str, size16 * sizeof(string.str[0]));
|
||||
|
||||
return str8_array(buffer, size16 + 1);
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_resource_string_from_str8(Arena *arena, String8 string)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String16 string16 = str16_from_8(scratch.arena, string);
|
||||
String8 result = coff_resource_string_from_str16(arena, string16);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_resource_number_from_u16(Arena *arena, U16 number)
|
||||
{
|
||||
U16 *buffer = push_array_no_zero(arena, U16, 2);
|
||||
buffer[0] = max_U16;
|
||||
buffer[1] = number;
|
||||
return str8_array(buffer, 2);
|
||||
}
|
||||
|
||||
internal COFF_ResourceID
|
||||
coff_utf8_resource_id_from_utf16(Arena *arena, COFF_ResourceID16 *id_16)
|
||||
{
|
||||
COFF_ResourceID id = {0};
|
||||
id.type = id_16->type;
|
||||
switch (id_16->type) {
|
||||
case COFF_ResourceIDType_Null: break;
|
||||
case COFF_ResourceIDType_Number: {
|
||||
id.u.number = id_16->u.number;
|
||||
} break;
|
||||
case COFF_ResourceIDType_String: {
|
||||
id.u.string = str8_from_16(arena, id_16->u.string);
|
||||
} break;
|
||||
default: InvalidPath;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
internal U64
|
||||
coff_read_resource_id_utf16(String8 raw_res, U64 off, COFF_ResourceID16 *id_out)
|
||||
{
|
||||
U64 cursor = off;
|
||||
|
||||
U16 flag = 0;
|
||||
str8_deserial_read_struct(raw_res, cursor, &flag);
|
||||
|
||||
if (flag == max_U16) {
|
||||
id_out->type = COFF_ResourceIDType_Number;
|
||||
cursor += sizeof(flag);
|
||||
cursor += str8_deserial_read_struct(raw_res, cursor, &id_out->u.number);
|
||||
} else {
|
||||
id_out->type = COFF_ResourceIDType_String;
|
||||
cursor += str8_deserial_read_windows_utf16_string16(raw_res, cursor, &id_out->u.string);
|
||||
}
|
||||
|
||||
U64 read_size = cursor - off;
|
||||
read_size = AlignPow2(read_size, COFF_ResourceAlign);
|
||||
return read_size;
|
||||
}
|
||||
|
||||
internal U64
|
||||
coff_read_resource(Arena *arena, String8 raw_res, U64 off, COFF_ParsedResource *res_out)
|
||||
{
|
||||
String8 raw_header = str8_skip(raw_res, off);
|
||||
U64 header_cursor = 0;
|
||||
|
||||
// prefix
|
||||
COFF_ResourceHeaderPrefix prefix = {0};
|
||||
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &prefix);
|
||||
|
||||
Assert(prefix.header_size >= sizeof(COFF_ResourceHeaderPrefix));
|
||||
raw_header = str8_prefix(raw_header, prefix.header_size);
|
||||
|
||||
// header
|
||||
COFF_ResourceID16 type_16 = {0};
|
||||
COFF_ResourceID16 name_16 = {0};
|
||||
header_cursor += coff_read_resource_id_utf16(raw_header, header_cursor, &type_16);
|
||||
header_cursor += coff_read_resource_id_utf16(raw_header, header_cursor, &name_16);
|
||||
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->data_version);
|
||||
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->memory_flags);
|
||||
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->language_id);
|
||||
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->version);
|
||||
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->characteristics);
|
||||
Assert(prefix.header_size == header_cursor);
|
||||
|
||||
// convert utf-16 resource ids to utf-8
|
||||
res_out->type = coff_utf8_resource_id_from_utf16(arena, &type_16);
|
||||
res_out->name = coff_utf8_resource_id_from_utf16(arena, &name_16);
|
||||
|
||||
// read data
|
||||
U64 data_read_size = str8_deserial_read_block(raw_res, off + prefix.header_size, prefix.data_size, &res_out->data);
|
||||
Assert(prefix.data_size == data_read_size);
|
||||
|
||||
// compute read size
|
||||
U64 read_size = Max(prefix.header_size, sizeof(prefix)) + AlignPow2(prefix.data_size, COFF_ResourceAlign);
|
||||
return read_size;
|
||||
}
|
||||
|
||||
internal COFF_ParsedResourceList
|
||||
coff_resource_list_from_data(Arena *arena, String8 data)
|
||||
{
|
||||
COFF_ParsedResourceList list = {0};
|
||||
U64 cursor;
|
||||
for (cursor = 0 ; cursor < data.size; ) {
|
||||
COFF_ParsedResourceNode *node = push_array(arena, COFF_ParsedResourceNode, 1);
|
||||
cursor += coff_read_resource(arena, data, cursor, &node->data);
|
||||
SLLQueuePush(list.first, list.last, node);
|
||||
++list.count;
|
||||
}
|
||||
Assert(cursor == data.size);
|
||||
return list;
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_write_resource_id(Arena *arena, COFF_ResourceID id)
|
||||
{
|
||||
String8 result = str8_zero();
|
||||
switch (id.type) {
|
||||
case COFF_ResourceIDType_Null: break;
|
||||
case COFF_ResourceIDType_Number: {
|
||||
result = coff_resource_number_from_u16(arena, id.u.number);
|
||||
} break;
|
||||
case COFF_ResourceIDType_String: {
|
||||
result = coff_resource_string_from_str8(arena, id.u.string);
|
||||
} break;
|
||||
default: InvalidPath;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_write_resource(Arena *arena,
|
||||
COFF_ResourceID type,
|
||||
COFF_ResourceID name,
|
||||
U32 data_version,
|
||||
COFF_ResourceMemoryFlags memory_flags,
|
||||
U16 language_id,
|
||||
U32 version,
|
||||
U32 characteristics,
|
||||
String8 data)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
String8List list = {0};
|
||||
|
||||
COFF_ResourceHeaderPrefix *prefix = push_array(scratch.arena, COFF_ResourceHeaderPrefix, 1);
|
||||
String8 packed_type = coff_write_resource_id(scratch.arena, type);
|
||||
String8 packed_name = coff_write_resource_id(scratch.arena, name);
|
||||
|
||||
// prefix + header
|
||||
str8_list_push(scratch.arena, &list, str8_struct(prefix));
|
||||
str8_list_push(scratch.arena, &list, packed_type);
|
||||
str8_list_push(scratch.arena, &list, packed_name);
|
||||
str8_list_push(scratch.arena, &list, str8_struct(&data_version));
|
||||
str8_list_push(scratch.arena, &list, str8_struct(&memory_flags));
|
||||
str8_list_push(scratch.arena, &list, str8_struct(&language_id));
|
||||
str8_list_push(scratch.arena, &list, str8_struct(&version));
|
||||
str8_list_push(scratch.arena, &list, str8_struct(&characteristics));
|
||||
|
||||
prefix->data_size = safe_cast_u32(data.size);
|
||||
prefix->header_size = safe_cast_u32(list.total_size);
|
||||
|
||||
// data
|
||||
str8_list_push(scratch.arena, &list, data);
|
||||
|
||||
// magic
|
||||
str8_list_push_front(scratch.arena, &list, str8_array_fixed(g_coff_res_magic));
|
||||
|
||||
// align
|
||||
U64 align_size = AlignPow2(list.total_size, COFF_ResourceAlign) - list.total_size;
|
||||
U8 *align = push_array(scratch.arena, U8, align_size);
|
||||
str8_list_push(scratch.arena, &list, str8(align, align_size));
|
||||
|
||||
// join
|
||||
String8 res = str8_list_join(arena, &list, 0);
|
||||
|
||||
scratch_end(scratch);
|
||||
return res;
|
||||
}
|
||||
|
||||
internal int
|
||||
coff_resource_id_compar(void *raw_a, void *raw_b)
|
||||
{
|
||||
int cmp;
|
||||
COFF_ResourceID *a = raw_a;
|
||||
COFF_ResourceID *b = raw_b;
|
||||
if (a->type == b->type) {
|
||||
switch (a->type) {
|
||||
default:
|
||||
case COFF_ResourceIDType_Null: cmp = 0; break;
|
||||
case COFF_ResourceIDType_Number: cmp = MemoryCompare(&a->u.number, &b->u.number, sizeof(a->u.number)); break;
|
||||
case COFF_ResourceIDType_String: cmp = strncmp((char *)a->u.string.str, (char *)b->u.string.str, Min(a->u.string.size, b->u.string.size)); break;
|
||||
}
|
||||
} else {
|
||||
cmp = a->type < b->type ? -1 : a->type > b->type ? +1 : 0;
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
|
||||
internal B32
|
||||
coff_is_import(String8 raw_archive_member)
|
||||
{
|
||||
B32 is_import = 0;
|
||||
if (raw_archive_member.size >= sizeof(U16)*2) {
|
||||
U16 *sig1 = (U16*)raw_archive_member.str;
|
||||
U16 *sig2 = sig1 + 1;
|
||||
is_import = *sig1 == COFF_MachineType_Unknown && *sig2 == 0xffff;
|
||||
}
|
||||
return is_import;
|
||||
}
|
||||
|
||||
internal COFF_DataType
|
||||
coff_data_type_from_data(String8 raw_archive_member)
|
||||
{
|
||||
B32 is_big_obj = coff_is_big_obj(raw_archive_member);
|
||||
if (is_big_obj) {
|
||||
return COFF_DataType_BigObj;
|
||||
}
|
||||
B32 is_import = coff_is_import(raw_archive_member);
|
||||
if (is_import) {
|
||||
return COFF_DataType_Import;
|
||||
}
|
||||
return COFF_DataType_Obj;
|
||||
}
|
||||
|
||||
internal B32
|
||||
coff_is_regular_archive(String8 raw_archive)
|
||||
{
|
||||
B32 is_archive = 0;
|
||||
U8 sig[sizeof(g_coff_archive_sig)];
|
||||
if (str8_deserial_read_struct(raw_archive, 0, &sig) == sizeof(sig)) {
|
||||
is_archive = MemoryCompare(&sig[0], &g_coff_archive_sig[0], sizeof(g_coff_archive_sig)) == 0;
|
||||
}
|
||||
return is_archive;
|
||||
}
|
||||
|
||||
internal B32
|
||||
coff_is_thin_archive(String8 raw_archive)
|
||||
{
|
||||
B32 is_archive = 0;
|
||||
U8 sig[sizeof(g_coff_thin_archive_sig)];
|
||||
if (str8_deserial_read_struct(raw_archive, 0, &sig) == sizeof(sig)) {
|
||||
is_archive = MemoryCompare(&sig[0], &g_coff_thin_archive_sig[0], sizeof(g_coff_thin_archive_sig)) == 0;
|
||||
}
|
||||
return is_archive;
|
||||
}
|
||||
|
||||
internal COFF_ArchiveType
|
||||
coff_archive_type_from_data(String8 raw_archive)
|
||||
{
|
||||
if (coff_is_regular_archive(raw_archive)) {
|
||||
return COFF_Archive_Regular;
|
||||
} else if (coff_is_thin_archive(raw_archive)) {
|
||||
return COFF_Archive_Thin;
|
||||
}
|
||||
return COFF_Archive_Null;
|
||||
}
|
||||
|
||||
internal U64
|
||||
coff_parse_archive_member_header(String8 raw_archive, U64 offset, COFF_ParsedArchiveMemberHeader *header_out)
|
||||
{
|
||||
COFF_ArchiveMemberHeader *header = str8_deserial_get_raw_ptr(raw_archive, offset, sizeof(*header));
|
||||
if (header) {
|
||||
String8 name = str8_skip_chop_whitespace(str8_cstring_capped(header->name, header->name + sizeof(header->name) ));
|
||||
String8 date = str8_skip_chop_whitespace(str8_cstring_capped(header->date, header->date + sizeof(header->date) ));
|
||||
String8 user_id = str8_skip_chop_whitespace(str8_cstring_capped(header->user_id, header->user_id + sizeof(header->user_id) ));
|
||||
String8 group_id = str8_skip_chop_whitespace(str8_cstring_capped(header->group_id, header->group_id + sizeof(header->group_id)));
|
||||
String8 mode = str8_skip_chop_whitespace(str8_cstring_capped(header->mode, header->mode + sizeof(header->mode) ));
|
||||
String8 size = str8_skip_chop_whitespace(str8_cstring_capped(header->size, header->size + sizeof(header->size) ));
|
||||
String8 end = str8_cstring_capped(header->end, header->end + sizeof(header->end));
|
||||
|
||||
U32 data_size = u32_from_str8(size, 10);
|
||||
U64 data_off = offset + sizeof(COFF_ArchiveMemberHeader);
|
||||
|
||||
header_out->name = name;
|
||||
header_out->time_stamp = u32_from_str8(date, 10);
|
||||
header_out->user_id = u32_from_str8(user_id, 10);
|
||||
header_out->group_id = u32_from_str8(group_id, 10);
|
||||
header_out->mode = mode;
|
||||
header_out->is_end_correct = str8_match_lit("`\n", end, 0);
|
||||
header_out->data_range = rng_1u64(data_off, data_off + data_size);
|
||||
|
||||
return sizeof(*header);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal COFF_ArchiveFirstMember
|
||||
coff_parse_first_archive_member(COFF_ArchiveMember *member)
|
||||
{
|
||||
Assert(str8_match_lit("/", member->header.name, 0));
|
||||
|
||||
U64 cursor = 0;
|
||||
|
||||
U32 symbol_count = 0;
|
||||
cursor += str8_deserial_read_struct(member->data, cursor, &symbol_count);
|
||||
|
||||
symbol_count = from_be_u32(symbol_count);
|
||||
|
||||
Rng1U64 member_offsets_range = rng_1u64(cursor, cursor + symbol_count * sizeof(U32));
|
||||
cursor += dim_1u64(member_offsets_range);
|
||||
|
||||
Rng1U64 string_table_range = rng_1u64(cursor, member->data.size);
|
||||
cursor += dim_1u64(string_table_range);
|
||||
|
||||
String8 raw_member_offsets = str8_substr(member->data, member_offsets_range);
|
||||
U32 *member_offsets = (U32 *)raw_member_offsets.str;
|
||||
U64 member_offset_count = raw_member_offsets.size / sizeof(member_offsets[0]);
|
||||
|
||||
COFF_ArchiveFirstMember result = {0};
|
||||
result.symbol_count = symbol_count;
|
||||
result.member_offset_count = member_offset_count;
|
||||
result.member_offsets = member_offsets;
|
||||
result.string_table = str8_substr(member->data, string_table_range);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal COFF_ArchiveSecondMember
|
||||
coff_parse_second_archive_member(COFF_ArchiveMember *member)
|
||||
{
|
||||
COFF_ArchiveSecondMember result = {0};
|
||||
|
||||
if (str8_match_lit("/", member->header.name, 0)) {
|
||||
U64 cursor = 0;
|
||||
|
||||
U32 member_count = 0;
|
||||
cursor += str8_deserial_read_struct(member->data, cursor, &member_count);
|
||||
|
||||
Rng1U64 member_offsets_range = rng_1u64(cursor, cursor + member_count * sizeof(U32));
|
||||
cursor += dim_1u64(member_offsets_range);
|
||||
|
||||
U32 symbol_count = 0;
|
||||
cursor += str8_deserial_read_struct(member->data, cursor, &symbol_count);
|
||||
|
||||
Rng1U64 symbol_indices_range = rng_1u64(cursor, cursor + symbol_count * sizeof(U16));
|
||||
cursor += dim_1u64(symbol_indices_range);
|
||||
|
||||
Rng1U64 string_table_range = rng_1u64(cursor, member->data.size);
|
||||
|
||||
String8 raw_member_offsets = str8_substr(member->data, member_offsets_range);
|
||||
String8 raw_indices = str8_substr(member->data, symbol_indices_range);
|
||||
|
||||
U32 *member_offsets = (U32 *)raw_member_offsets.str;
|
||||
U64 member_offset_count = raw_member_offsets.size / sizeof(member_offsets[0]);
|
||||
|
||||
U16 *symbol_indices = (U16 *)raw_indices.str;
|
||||
U64 symbol_index_count = raw_indices.size / sizeof(symbol_indices[0]);
|
||||
|
||||
result.member_count = member_count;
|
||||
result.symbol_count = symbol_count;
|
||||
result.member_offsets = member_offsets;
|
||||
result.member_offset_count = member_offset_count;
|
||||
result.symbol_indices = symbol_indices;
|
||||
result.symbol_index_count = symbol_index_count;
|
||||
result.string_table = str8_substr(member->data, string_table_range);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_decode_raw_member_name(String8 long_names, String8 name)
|
||||
{
|
||||
String8 result = name;
|
||||
if (name.size > 0 && name.str[0] == '/') {
|
||||
String8 offset_str = str8(name.str + 1, name.size - 1);
|
||||
U64 offset = u64_from_str8(offset_str, 10);
|
||||
if (offset < long_names.size) {
|
||||
U8 *ptr = long_names.str + offset;
|
||||
U8 *opl = long_names.str + long_names.size;
|
||||
for (; ptr < opl; ++ptr) {
|
||||
if (*ptr == '\0' || *ptr == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = str8_range(long_names.str + offset, ptr);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
coff_decode_member_name(String8 long_names, String8 name)
|
||||
{
|
||||
String8 member_name = coff_decode_raw_member_name(long_names, name);
|
||||
String8 slash = str8_lit("/");
|
||||
if (str8_ends_with(member_name, slash, 0)) {
|
||||
member_name = str8_chop(member_name, slash.size);
|
||||
}
|
||||
return member_name;
|
||||
}
|
||||
|
||||
internal U64
|
||||
coff_parse_import(String8 raw_archive_member, U64 offset, COFF_ParsedArchiveImportHeader *header_out)
|
||||
{
|
||||
COFF_ImportHeader *header = str8_deserial_get_raw_ptr(raw_archive_member, offset, sizeof(*header));
|
||||
if (header) {
|
||||
Rng1U64 data_range = rng_1u64(offset + sizeof(*header), offset + sizeof(*header) + header->data_size);
|
||||
String8 raw_data = str8_substr(raw_archive_member, data_range);
|
||||
U64 data_cursor = 0;
|
||||
|
||||
header_out->version = header->version;
|
||||
header_out->machine = header->machine;
|
||||
header_out->time_stamp = header->time_stamp;
|
||||
header_out->data_size = header->data_size;
|
||||
header_out->hint_or_ordinal = header->hint_or_ordinal;
|
||||
header_out->type = COFF_ImportHeader_ExtractType(header->flags);
|
||||
header_out->import_by = COFF_ImportHeader_ExtractImportBy(header->flags);
|
||||
data_cursor += str8_deserial_read_cstr(raw_data, data_cursor, &header_out->func_name);
|
||||
data_cursor += str8_deserial_read_cstr(raw_data, data_cursor, &header_out->dll_name);
|
||||
|
||||
Assert(header_out->func_name.size + header_out->dll_name.size + /* nulls */ 2 == header_out->data_size);
|
||||
U64 read_size = sizeof(*header) + header->data_size;
|
||||
return read_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal COFF_ArchiveMember
|
||||
coff_archive_member_from_offset(String8 raw_archive, U64 offset)
|
||||
{
|
||||
COFF_ArchiveMember member = {0};
|
||||
coff_regular_archive_member_iter_next(raw_archive, &offset, &member);
|
||||
return member;
|
||||
}
|
||||
|
||||
internal COFF_ArchiveMember
|
||||
coff_archive_member_from_data(String8 raw_archive_member)
|
||||
{
|
||||
return coff_archive_member_from_offset(raw_archive_member, 0);
|
||||
}
|
||||
|
||||
internal COFF_ParsedArchiveImportHeader
|
||||
coff_archive_import_from_data(String8 raw_archive_member)
|
||||
{
|
||||
COFF_ParsedArchiveImportHeader header = {0};
|
||||
coff_parse_import(raw_archive_member, 0, &header);
|
||||
return header;
|
||||
}
|
||||
|
||||
internal U64
|
||||
coff_regular_archive_member_iter_init(String8 raw_archive)
|
||||
{
|
||||
U64 cursor = raw_archive.size;
|
||||
if (coff_is_regular_archive(raw_archive)) {
|
||||
cursor = sizeof(g_coff_archive_sig);
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
internal B32
|
||||
coff_regular_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out)
|
||||
{
|
||||
B32 is_parsed = 0;
|
||||
|
||||
member_out->header.is_end_correct = 0;
|
||||
U64 header_size = coff_parse_archive_member_header(raw_archive, *offset, &member_out->header);
|
||||
|
||||
if (member_out->header.is_end_correct) {
|
||||
member_out->offset = *offset;
|
||||
member_out->data = str8_substr(raw_archive, member_out->header.data_range);
|
||||
|
||||
U64 read_size = AlignPow2(header_size + dim_1u64(member_out->header.data_range), COFF_Archive_MemberAlign);
|
||||
*offset += read_size;
|
||||
|
||||
is_parsed = 1;
|
||||
}
|
||||
|
||||
return is_parsed;
|
||||
}
|
||||
|
||||
internal U64
|
||||
coff_thin_archive_member_iter_init(String8 raw_archive)
|
||||
{
|
||||
U64 cursor = raw_archive.size;
|
||||
if (coff_is_thin_archive(raw_archive)) {
|
||||
cursor = sizeof(g_coff_thin_archive_sig);
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
internal B32
|
||||
coff_thin_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out)
|
||||
{
|
||||
B32 is_parsed = 0;
|
||||
|
||||
member_out->header.is_end_correct = 0;
|
||||
U64 header_size = coff_parse_archive_member_header(raw_archive, *offset, &member_out->header);
|
||||
|
||||
if (member_out->header.is_end_correct) {
|
||||
Rng1U64 data_in_archive_range = {0};
|
||||
if (str8_match_lit("/", member_out->header.name, 0) || str8_match_lit("//", member_out->header.name, 0)) {
|
||||
data_in_archive_range = member_out->header.data_range;
|
||||
}
|
||||
|
||||
member_out->offset = *offset;
|
||||
member_out->data = str8_substr(raw_archive, data_in_archive_range);
|
||||
|
||||
U64 read_size = AlignPow2(header_size + dim_1u64(data_in_archive_range), COFF_Archive_MemberAlign);
|
||||
*offset += read_size;
|
||||
|
||||
is_parsed = 1;
|
||||
}
|
||||
|
||||
return is_parsed;
|
||||
}
|
||||
|
||||
internal void
|
||||
coff_archive_member_list_push_node(COFF_ArchiveMemberList *list, COFF_ArchiveMemberNode *node)
|
||||
{
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
internal COFF_ArchiveParse
|
||||
coff_archive_parse_from_member_list(COFF_ArchiveMemberList member_list)
|
||||
{
|
||||
String8 error = str8_zero();
|
||||
B32 has_second_header = 0;
|
||||
B32 has_long_names = 0;
|
||||
COFF_ArchiveMember first_header = {0};
|
||||
COFF_ArchiveMember second_header = {0};
|
||||
COFF_ArchiveMember long_names_member = {0};
|
||||
|
||||
COFF_ArchiveMemberNode *ptr = member_list.first;
|
||||
|
||||
if (ptr) {
|
||||
if (str8_match_lit("/", ptr->data.header.name, 0)) {
|
||||
if (ptr->data.header.is_end_correct) {
|
||||
first_header = ptr->data;
|
||||
ptr = ptr->next;
|
||||
} else {
|
||||
error = str8_lit("first header doesn't have correct end");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error = str8_lit("missing first header");
|
||||
}
|
||||
|
||||
if (!error.size && ptr) {
|
||||
if (str8_match_lit("/", ptr->data.header.name, 0)) {
|
||||
if (ptr->data.header.is_end_correct) {
|
||||
second_header = ptr->data;
|
||||
ptr = ptr->next;
|
||||
has_second_header = 1;
|
||||
} else {
|
||||
error = str8_lit("second header doesn't have correct end");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!error.size && ptr) {
|
||||
if (str8_match_lit("//", ptr->data.header.name, 0)) {
|
||||
if (ptr->data.header.is_end_correct) {
|
||||
long_names_member = ptr->data;
|
||||
ptr = ptr->next;
|
||||
has_long_names;
|
||||
} else {
|
||||
error = str8_lit("long names header doesn't have correct end");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
COFF_ArchiveParse parse = {0};
|
||||
parse.has_second_header = has_second_header;
|
||||
parse.has_long_names = has_long_names;
|
||||
parse.first_member = coff_parse_first_archive_member(&first_header);
|
||||
parse.second_member = coff_parse_second_archive_member(&second_header);
|
||||
parse.long_names = long_names_member.data;
|
||||
parse.error = error;
|
||||
|
||||
return parse;
|
||||
}
|
||||
|
||||
internal COFF_ArchiveParse
|
||||
coff_regular_archive_parse_from_data(String8 raw_archive)
|
||||
{
|
||||
COFF_ArchiveMemberList list = {0};
|
||||
COFF_ArchiveMemberNode node_arr[3] = {0};
|
||||
U64 cursor = coff_regular_archive_member_iter_init(raw_archive);
|
||||
for (U64 i = 0; i < ArrayCount(node_arr); ++i) {
|
||||
COFF_ArchiveMemberNode *node = &node_arr[i];
|
||||
if (!coff_regular_archive_member_iter_next(raw_archive, &cursor, &node->data)) {
|
||||
break;
|
||||
}
|
||||
coff_archive_member_list_push_node(&list, node);
|
||||
}
|
||||
return coff_archive_parse_from_member_list(list);
|
||||
}
|
||||
|
||||
internal COFF_ArchiveParse
|
||||
coff_thin_archive_parse_from_data(String8 raw_archive)
|
||||
{
|
||||
COFF_ArchiveMemberList list = {0};
|
||||
COFF_ArchiveMemberNode node_arr[3] = {0};
|
||||
U64 cursor = coff_thin_archive_member_iter_init(raw_archive);
|
||||
for (U64 i = 0; i < ArrayCount(node_arr); i += 1) {
|
||||
COFF_ArchiveMemberNode *node = &node_arr[i];
|
||||
if (!coff_thin_archive_member_iter_next(raw_archive, &cursor, &node->data)) {
|
||||
break;
|
||||
}
|
||||
coff_archive_member_list_push_node(&list, node);
|
||||
}
|
||||
return coff_archive_parse_from_member_list(list);
|
||||
}
|
||||
|
||||
internal COFF_ArchiveParse
|
||||
coff_archive_parse_from_data(String8 raw_archive)
|
||||
{
|
||||
COFF_ArchiveType type = coff_archive_type_from_data(raw_archive);
|
||||
switch (type) {
|
||||
case COFF_Archive_Null: break;
|
||||
case COFF_Archive_Regular: return coff_regular_archive_parse_from_data(raw_archive);
|
||||
case COFF_Archive_Thin: return coff_thin_archive_parse_from_data(raw_archive);
|
||||
}
|
||||
COFF_ArchiveParse null_parse = {0};
|
||||
return null_parse;
|
||||
}
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef COFF_PARSE_H
|
||||
#define COFF_PARSE_H
|
||||
|
||||
typedef struct COFF_FileHeaderInfo
|
||||
{
|
||||
B32 is_big_obj;
|
||||
COFF_MachineType machine;
|
||||
U64 header_size;
|
||||
U64 section_count_no_null;
|
||||
U64 symbol_size;
|
||||
U64 symbol_count;
|
||||
Rng1U64 header_range;
|
||||
Rng1U64 section_table_range;
|
||||
Rng1U64 symbol_table_range;
|
||||
Rng1U64 string_table_range;
|
||||
} COFF_FileHeaderInfo;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
typedef struct COFF_SectionHeaderArray
|
||||
{
|
||||
U64 count;
|
||||
COFF_SectionHeader *v;
|
||||
} COFF_SectionHeaderArray;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
typedef struct COFF_Symbol16Node
|
||||
{
|
||||
struct COFF_Symbol16Node *next;
|
||||
COFF_Symbol16 data;
|
||||
} COFF_Symbol16Node;
|
||||
|
||||
typedef struct COFF_Symbol16List
|
||||
{
|
||||
U64 count;
|
||||
COFF_Symbol16Node *first;
|
||||
COFF_Symbol16Node *last;
|
||||
} COFF_Symbol16List;
|
||||
|
||||
typedef struct COFF_Symbol32Array
|
||||
{
|
||||
U64 count;
|
||||
COFF_Symbol32 *v;
|
||||
} COFF_Symbol32Array;
|
||||
|
||||
typedef struct COFF_ParsedSymbol
|
||||
{
|
||||
String8 name;
|
||||
U64 value;
|
||||
U32 section_number;
|
||||
COFF_SymbolType type;
|
||||
COFF_SymStorageClass storage_class;
|
||||
U8 aux_symbol_count;
|
||||
void *raw_symbol;
|
||||
} COFF_ParsedSymbol;
|
||||
|
||||
typedef U32 COFF_SymbolValueInterpType;
|
||||
enum
|
||||
{
|
||||
COFF_SymbolValueInterp_Regular, // symbol has section and offset.
|
||||
COFF_SymbolValueInterp_Weak, // symbol is overridable.
|
||||
COFF_SymbolValueInterp_Undefined, // symbol doesn't have a reference section.
|
||||
COFF_SymbolValueInterp_Common, // symbol has no section but still has size.
|
||||
COFF_SymbolValueInterp_Abs, // symbol has an absolute (non-relocatable) value and is not an address.
|
||||
COFF_SymbolValueInterp_Debug // symbol is used to provide general type of debugging information.
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
typedef struct COFF_RelocNode
|
||||
{
|
||||
struct COFF_RelocNode *next;
|
||||
COFF_Reloc data;
|
||||
} COFF_RelocNode;
|
||||
|
||||
typedef struct COFF_RelocList
|
||||
{
|
||||
U64 count;
|
||||
COFF_RelocNode *first;
|
||||
COFF_RelocNode *last;
|
||||
} COFF_RelocList;
|
||||
|
||||
typedef struct COFF_RelocArray
|
||||
{
|
||||
U64 count;
|
||||
COFF_Reloc *v;
|
||||
} COFF_RelocArray;
|
||||
|
||||
typedef struct COFF_RelocInfo
|
||||
{
|
||||
U64 array_off;
|
||||
U64 count;
|
||||
} COFF_RelocInfo;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
typedef U32 COFF_ResourceIDType;
|
||||
enum COFF_ResourceIDTypeEnum
|
||||
{
|
||||
COFF_ResourceIDType_Null,
|
||||
COFF_ResourceIDType_Number,
|
||||
COFF_ResourceIDType_String,
|
||||
COFF_ResourceIDType_Count
|
||||
};
|
||||
|
||||
typedef struct COFF_ResourceID16
|
||||
{
|
||||
COFF_ResourceIDType type;
|
||||
union {
|
||||
U16 number;
|
||||
String16 string;
|
||||
} u;
|
||||
} COFF_ResourceID16;
|
||||
|
||||
typedef struct COFF_ResourceID
|
||||
{
|
||||
COFF_ResourceIDType type;
|
||||
union {
|
||||
U16 number;
|
||||
String8 string;
|
||||
} u;
|
||||
} COFF_ResourceID;
|
||||
|
||||
typedef struct COFF_ParsedResource
|
||||
{
|
||||
COFF_ResourceID type;
|
||||
COFF_ResourceID name;
|
||||
U32 data_version;
|
||||
COFF_ResourceMemoryFlags memory_flags;
|
||||
U16 language_id;
|
||||
U32 version;
|
||||
U32 characteristics;
|
||||
String8 data;
|
||||
} COFF_ParsedResource;
|
||||
|
||||
typedef struct COFF_ParsedResourceNode
|
||||
{
|
||||
struct COFF_ParsedResourceNode *next;
|
||||
COFF_ParsedResource data;
|
||||
} COFF_ParsedResourceNode;
|
||||
|
||||
typedef struct COFF_ParsedResourceList
|
||||
{
|
||||
U64 count;
|
||||
COFF_ParsedResourceNode *first;
|
||||
COFF_ParsedResourceNode *last;
|
||||
} COFF_ParsedResourceList;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COFF_DataType_Null,
|
||||
COFF_DataType_Obj,
|
||||
COFF_DataType_BigObj,
|
||||
COFF_DataType_Import
|
||||
} COFF_DataType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COFF_Archive_Null,
|
||||
COFF_Archive_Regular,
|
||||
COFF_Archive_Thin
|
||||
} COFF_ArchiveType;
|
||||
|
||||
typedef struct COFF_ParsedArchiveMemberHeader
|
||||
{
|
||||
String8 name; // padded to 16 bytes with spaces
|
||||
COFF_TimeStamp time_stamp;
|
||||
U32 user_id; // unix artifact that does not have meaning on windows
|
||||
U32 group_id; // unix artifact that does not have meaning on windows
|
||||
String8 mode; // octal representation the members file mode
|
||||
B32 is_end_correct; // set to true if found correct signature after header
|
||||
Rng1U64 data_range;
|
||||
} COFF_ParsedArchiveMemberHeader;
|
||||
|
||||
typedef struct COFF_ParsedArchiveImportHeader
|
||||
{
|
||||
B32 is_sig_correct;
|
||||
U16 version;
|
||||
COFF_MachineType machine;
|
||||
COFF_TimeStamp time_stamp;
|
||||
U32 data_size;
|
||||
U16 hint_or_ordinal;
|
||||
COFF_ImportType type;
|
||||
COFF_ImportByType import_by;
|
||||
String8 func_name;
|
||||
String8 dll_name;
|
||||
} COFF_ParsedArchiveImportHeader;
|
||||
|
||||
typedef struct COFF_ArchiveMember
|
||||
{
|
||||
COFF_ParsedArchiveMemberHeader header;
|
||||
U64 offset;
|
||||
String8 data;
|
||||
} COFF_ArchiveMember;
|
||||
|
||||
typedef struct COFF_ArchiveFirstMember
|
||||
{
|
||||
U32 symbol_count;
|
||||
U64 member_offset_count;
|
||||
U32 *member_offsets;
|
||||
String8 string_table;
|
||||
} COFF_ArchiveFirstMember;
|
||||
|
||||
typedef struct COFF_ArchiveSecondMember
|
||||
{
|
||||
U32 member_count;
|
||||
U32 symbol_count;
|
||||
U64 member_offset_count;
|
||||
U32 *member_offsets;
|
||||
U64 symbol_index_count;
|
||||
U16 *symbol_indices;
|
||||
String8 string_table;
|
||||
} COFF_ArchiveSecondMember;
|
||||
|
||||
typedef struct COFF_ArchiveMemberNode
|
||||
{
|
||||
struct COFF_ArchiveMemberNode *next;
|
||||
COFF_ArchiveMember data;
|
||||
} COFF_ArchiveMemberNode;
|
||||
|
||||
typedef struct COFF_ArchiveMemberList
|
||||
{
|
||||
U64 count;
|
||||
COFF_ArchiveMemberNode *first;
|
||||
COFF_ArchiveMemberNode *last;
|
||||
} COFF_ArchiveMemberList;
|
||||
|
||||
typedef struct COFF_ArchiveParse
|
||||
{
|
||||
B32 has_second_header;
|
||||
B32 has_long_names;
|
||||
COFF_ArchiveFirstMember first_member;
|
||||
COFF_ArchiveSecondMember second_member;
|
||||
String8 long_names;
|
||||
String8 error;
|
||||
} COFF_ArchiveParse;
|
||||
|
||||
////////////////////////////////
|
||||
// Obj Header
|
||||
|
||||
internal B32 coff_is_big_obj(String8 raw_coff);
|
||||
internal B32 coff_is_obj (String8 raw_coff);
|
||||
internal COFF_FileHeaderInfo coff_file_header_info_from_data(String8 raw_coff);
|
||||
|
||||
////////////////////////////////
|
||||
// Section
|
||||
|
||||
internal COFF_SectionHeader ** coff_section_table_from_data(Arena *arena, String8 data, Rng1U64 section_table_range);
|
||||
|
||||
////////////////////////////////
|
||||
// Symbol
|
||||
|
||||
internal COFF_ParsedSymbol coff_parse_symbol32(String8 string_table, COFF_Symbol32 *sym32);
|
||||
internal COFF_ParsedSymbol coff_parse_symbol16(String8 string_table, COFF_Symbol16 *sym16);
|
||||
internal COFF_ParsedSymbol coff_parse_symbol(COFF_FileHeaderInfo header, String8 string_table, String8 symbol_table, U32 symbol_idx);
|
||||
|
||||
internal COFF_Symbol32Array coff_symbol_array_from_data_16(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count);
|
||||
internal COFF_Symbol32Array coff_symbol_array_from_data_32(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count);
|
||||
internal COFF_Symbol32Array coff_symbol_array_from_data (Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count, U64 symbol_size);
|
||||
|
||||
internal COFF_Symbol16Node *coff_symbol16_list_push(Arena *arena, COFF_Symbol16List *list, COFF_Symbol16 symbol);
|
||||
|
||||
internal COFF_SymbolValueInterpType coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_class);
|
||||
internal COFF_SymbolValueInterpType coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol);
|
||||
internal B32 coff_is_undefined_data_symbol(COFF_ParsedSymbol symbol);
|
||||
|
||||
internal void coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out);
|
||||
internal COFF_SymbolWeakExt * coff_parse_weak_tag(COFF_ParsedSymbol symbol, B32 is_big_obj);
|
||||
|
||||
////////////////////////////////
|
||||
// Reloc
|
||||
|
||||
internal COFF_RelocInfo coff_reloc_info_from_section_header(String8 data, COFF_SectionHeader *header);
|
||||
|
||||
////////////////////////////////
|
||||
// Resource
|
||||
|
||||
internal String8 coff_resource_string_from_str16 (Arena *arena, String16 string);
|
||||
internal String8 coff_resource_string_from_str8 (Arena *arena, String8 string);
|
||||
internal String8 coff_resource_number_from_u16 (Arena *arena, U16 number);
|
||||
internal COFF_ResourceID coff_utf8_resource_id_from_utf16(Arena *arena, COFF_ResourceID16 *id_16);
|
||||
|
||||
internal U64 coff_read_resource_id_utf16 (String8 raw_res, U64 off, COFF_ResourceID16 *id_out);
|
||||
internal U64 coff_read_resource (Arena *arena, String8 raw_res, U64 off, COFF_ParsedResource *res_out);
|
||||
internal COFF_ParsedResourceList coff_resource_list_from_data(Arena *arena, String8 data);
|
||||
|
||||
internal String8 coff_write_resource_id(Arena *arena, COFF_ResourceID id);
|
||||
internal String8 coff_write_resource (Arena *arena, COFF_ResourceID type, COFF_ResourceID name, U32 data_version, COFF_ResourceMemoryFlags memory_flags, U16 language_id, U32 version, U32 characteristics, String8 data);
|
||||
|
||||
internal int coff_resource_id_compar(void *raw_a, void *raw_b); // COFF_ResourceID
|
||||
|
||||
////////////////////////////////
|
||||
// Archive
|
||||
|
||||
internal B32 coff_is_import (String8 raw_archive_member);
|
||||
internal COFF_DataType coff_data_type_from_data (String8 raw_archive_member);
|
||||
internal B32 coff_is_regular_archive (String8 raw_archive);
|
||||
internal B32 coff_is_thin_archive (String8 raw_archive);
|
||||
internal COFF_ArchiveType coff_archive_type_from_data(String8 raw_archive);
|
||||
|
||||
internal U64 coff_parse_archive_member_header(String8 raw_archive, U64 offset, COFF_ParsedArchiveMemberHeader *header_out);
|
||||
internal COFF_ArchiveFirstMember coff_parse_first_archive_member (COFF_ArchiveMember *member);
|
||||
internal COFF_ArchiveSecondMember coff_parse_second_archive_member(COFF_ArchiveMember *member);
|
||||
internal String8 coff_decode_raw_member_name (String8 long_names, String8 name);
|
||||
internal String8 coff_decode_member_name (String8 long_names, String8 name);
|
||||
internal U64 coff_parse_import (String8 raw_archive_member, U64 offset, COFF_ParsedArchiveImportHeader *header_out);
|
||||
|
||||
internal COFF_ArchiveMember coff_archive_member_from_offset(String8 raw_archive, U64 offset);
|
||||
internal COFF_ArchiveMember coff_archive_member_from_data (String8 raw_archive_member);
|
||||
internal COFF_ParsedArchiveImportHeader coff_archive_import_from_data (String8 raw_archive_member);
|
||||
|
||||
internal U64 coff_regular_archive_member_iter_init(String8 raw_archive);
|
||||
internal B32 coff_regular_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out);
|
||||
|
||||
internal U64 coff_thin_archive_member_iter_init(String8 raw_archive);
|
||||
internal B32 coff_thin_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out);
|
||||
|
||||
internal COFF_ArchiveParse coff_regular_archive_parse_from_member_list(COFF_ArchiveMemberList list);
|
||||
internal COFF_ArchiveParse coff_thin_archive_parse_from_data (String8 raw_archive);
|
||||
internal COFF_ArchiveParse coff_regular_archive_parse_from_data (String8 raw_archive);
|
||||
internal COFF_ArchiveParse coff_archive_parse_from_data (String8 raw_archive);
|
||||
|
||||
#endif // COFF_PARSE_H
|
||||
@@ -1,111 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal CFG_KeyMap *
|
||||
cfg_key_map_from_cfg(Arena *arena)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
CFG_KeyMap *key_map = push_array(arena, CFG_KeyMap, 1);
|
||||
{
|
||||
key_map->name_slots_count = 4096;
|
||||
key_map->name_slots = push_array(arena, CFG_KeyMapSlot, key_map->name_slots_count);
|
||||
key_map->binding_slots_count = 4096;
|
||||
key_map->binding_slots = push_array(arena, CFG_KeyMapSlot, key_map->binding_slots_count);
|
||||
|
||||
//- rjf: gather & parse all explicitly stored keybinding sets
|
||||
CFG_NodePtrList keybindings_cfg_list = cfg_node_top_level_list_from_string(scratch.arena, str8_lit("keybindings"));
|
||||
for(CFG_NodePtrNode *n = keybindings_cfg_list.first; n != 0; n = n->next)
|
||||
{
|
||||
CFG_Node *keybindings_root = n->v;
|
||||
for(CFG_Node *keybinding = keybindings_root->first; keybinding != &cfg_nil_node; keybinding = keybinding->next)
|
||||
{
|
||||
String8 name = {0};
|
||||
CFG_Binding binding = {0};
|
||||
for(CFG_Node *child = keybinding->first; child != &cfg_nil_node; child = child->next)
|
||||
{
|
||||
if(0){}
|
||||
else if(str8_match(child->string, str8_lit("ctrl"), 0)) { binding.modifiers |= OS_Modifier_Ctrl; }
|
||||
else if(str8_match(child->string, str8_lit("alt"), 0)) { binding.modifiers |= OS_Modifier_Alt; }
|
||||
else if(str8_match(child->string, str8_lit("shift"), 0)) { binding.modifiers |= OS_Modifier_Shift; }
|
||||
else
|
||||
{
|
||||
OS_Key key = OS_Key_Null;
|
||||
for EachEnumVal(OS_Key, k)
|
||||
{
|
||||
if(str8_match(child->string, os_g_key_cfg_string_table[k], StringMatchFlag_CaseInsensitive))
|
||||
{
|
||||
key = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(key != OS_Key_Null)
|
||||
{
|
||||
binding.key = key;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = child->string;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(name.size != 0)
|
||||
{
|
||||
U64 name_hash = d_hash_from_string(name);
|
||||
U64 binding_hash = d_hash_from_string(str8_struct(&binding));
|
||||
U64 name_slot_idx = name_hash%key_map->name_slots_count;
|
||||
U64 binding_slot_idx = binding_hash%key_map->binding_slots_count;
|
||||
CFG_KeyMapNode *n = push_array(arena, CFG_KeyMapNode, 1);
|
||||
n->cfg_id = keybinding->id;
|
||||
n->name = push_str8_copy(arena, name);
|
||||
n->binding = binding;
|
||||
SLLQueuePush_N(key_map->name_slots[name_slot_idx].first, key_map->name_slots[name_slot_idx].last, n, name_hash_next);
|
||||
SLLQueuePush_N(key_map->binding_slots[binding_slot_idx].first, key_map->binding_slots[binding_slot_idx].last, n, binding_hash_next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return key_map;
|
||||
}
|
||||
|
||||
internal CFG_KeyMapNodePtrList
|
||||
cfg_key_map_node_ptr_list_from_name(Arena *arena, CFG_KeyMap *key_map, String8 string)
|
||||
{
|
||||
CFG_KeyMapNodePtrList list = {0};
|
||||
{
|
||||
U64 hash = d_hash_from_string(string);
|
||||
U64 slot_idx = hash%key_map->name_slots_count;
|
||||
for(CFG_KeyMapNode *n = key_map->name_slots[slot_idx].first; n != 0; n = n->name_hash_next)
|
||||
{
|
||||
if(str8_match(n->name, string, 0))
|
||||
{
|
||||
CFG_KeyMapNodePtr *ptr = push_array(arena, CFG_KeyMapNodePtr, 1);
|
||||
ptr->v = n;
|
||||
SLLQueuePush(list.first, list.last, ptr);
|
||||
list.count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
internal CFG_KeyMapNodePtrList
|
||||
cfg_key_map_node_ptr_list_from_binding(Arena *arena, CFG_KeyMap *key_map, CFG_Binding binding)
|
||||
{
|
||||
CFG_KeyMapNodePtrList list = {0};
|
||||
{
|
||||
U64 hash = d_hash_from_string(str8_struct(&binding));
|
||||
U64 slot_idx = hash%key_map->binding_slots_count;
|
||||
for(CFG_KeyMapNode *n = key_map->binding_slots[slot_idx].first; n != 0; n = n->binding_hash_next)
|
||||
{
|
||||
if(MemoryMatchStruct(&binding, &n->binding))
|
||||
{
|
||||
CFG_KeyMapNodePtr *ptr = push_array(arena, CFG_KeyMapNodePtr, 1);
|
||||
ptr->v = n;
|
||||
SLLQueuePush(list.first, list.last, ptr);
|
||||
list.count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CONFIG_BINDINGS_H
|
||||
#define CONFIG_BINDINGS_H
|
||||
|
||||
typedef struct CFG_Binding CFG_Binding;
|
||||
struct CFG_Binding
|
||||
{
|
||||
OS_Key key;
|
||||
OS_Modifiers modifiers;
|
||||
};
|
||||
|
||||
typedef struct CFG_KeyMapNode CFG_KeyMapNode;
|
||||
struct CFG_KeyMapNode
|
||||
{
|
||||
CFG_KeyMapNode *name_hash_next;
|
||||
CFG_KeyMapNode *binding_hash_next;
|
||||
CFG_ID cfg_id;
|
||||
String8 name;
|
||||
CFG_Binding binding;
|
||||
};
|
||||
|
||||
typedef struct CFG_KeyMapNodePtr CFG_KeyMapNodePtr;
|
||||
struct CFG_KeyMapNodePtr
|
||||
{
|
||||
CFG_KeyMapNodePtr *next;
|
||||
CFG_KeyMapNode *v;
|
||||
};
|
||||
|
||||
typedef struct CFG_KeyMapNodePtrList CFG_KeyMapNodePtrList;
|
||||
struct CFG_KeyMapNodePtrList
|
||||
{
|
||||
CFG_KeyMapNodePtr *first;
|
||||
CFG_KeyMapNodePtr *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct CFG_KeyMapSlot CFG_KeyMapSlot;
|
||||
struct CFG_KeyMapSlot
|
||||
{
|
||||
CFG_KeyMapNode *first;
|
||||
CFG_KeyMapNode *last;
|
||||
};
|
||||
|
||||
typedef struct CFG_KeyMap CFG_KeyMap;
|
||||
struct CFG_KeyMap
|
||||
{
|
||||
U64 name_slots_count;
|
||||
CFG_KeyMapSlot *name_slots;
|
||||
U64 binding_slots_count;
|
||||
CFG_KeyMapSlot *binding_slots;
|
||||
};
|
||||
|
||||
internal CFG_KeyMap *cfg_key_map_from_cfg(Arena *arena);
|
||||
internal CFG_KeyMapNodePtrList cfg_key_map_node_ptr_list_from_name(Arena *arena, CFG_KeyMap *key_map, String8 string);
|
||||
internal CFG_KeyMapNodePtrList cfg_key_map_node_ptr_list_from_binding(Arena *arena, CFG_KeyMap *key_map, CFG_Binding binding);
|
||||
|
||||
#endif // CONFIG_BINDINGS_H
|
||||
@@ -1,896 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: ID Functions
|
||||
|
||||
internal void
|
||||
cfg_id_list_push(Arena *arena, CFG_IDList *list, CFG_ID id)
|
||||
{
|
||||
CFG_IDNode *n = push_array(arena, CFG_IDNode, 1);
|
||||
SLLQueuePush(list->first, list->last, n);
|
||||
list->count += 1;
|
||||
n->v = id;
|
||||
}
|
||||
|
||||
internal CFG_IDList
|
||||
cfg_id_list_copy(Arena *arena, CFG_IDList *src)
|
||||
{
|
||||
CFG_IDList dst = {0};
|
||||
for EachNode(n, CFG_IDNode, src->first)
|
||||
{
|
||||
cfg_id_list_push(arena, &dst, n->v);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Node Pointer Data Structure Functions
|
||||
|
||||
internal void
|
||||
cfg_node_ptr_list_push(Arena *arena, CFG_NodePtrList *list, CFG_Node *node)
|
||||
{
|
||||
CFG_NodePtrNode *n = push_array(arena, CFG_NodePtrNode, 1);
|
||||
DLLPushBack(list->first, list->last, n);
|
||||
list->count += 1;
|
||||
n->v = node;
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_node_ptr_list_push_front(Arena *arena, CFG_NodePtrList *list, CFG_Node *node)
|
||||
{
|
||||
CFG_NodePtrNode *n = push_array(arena, CFG_NodePtrNode, 1);
|
||||
DLLPushFront(list->first, list->last, n);
|
||||
list->count += 1;
|
||||
n->v = node;
|
||||
}
|
||||
|
||||
internal CFG_NodePtrArray
|
||||
cfg_node_ptr_array_from_list(Arena *arena, CFG_NodePtrList *list)
|
||||
{
|
||||
CFG_NodePtrArray array = {0};
|
||||
array.count = list->count;
|
||||
array.v = push_array_no_zero(arena, CFG_Node *, array.count);
|
||||
{
|
||||
U64 idx = 0;
|
||||
for EachNode(n, CFG_NodePtrNode, list->first)
|
||||
{
|
||||
array.v[idx] = n->v;
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Schema Data Structure Functions
|
||||
|
||||
internal void
|
||||
cfg_schema_table_insert(Arena *arena, CFG_SchemaTable *table, String8 name, MD_Node *schema)
|
||||
{
|
||||
U64 hash = u64_hash_from_str8(name);
|
||||
U64 slot_idx = hash%table->slots_count;
|
||||
CFG_SchemaNode *node = 0;
|
||||
for(CFG_SchemaNode *n = table->slots[slot_idx]; n != 0; n = n->next)
|
||||
{
|
||||
if(str8_match(n->name, name, 0))
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(node == 0)
|
||||
{
|
||||
node = push_array(arena, CFG_SchemaNode, 1);
|
||||
node->name = str8_copy(arena, name);
|
||||
node->schema = schema;
|
||||
SLLStackPush(table->slots[slot_idx], node);
|
||||
}
|
||||
}
|
||||
|
||||
internal MD_Node *
|
||||
cfg_schema_from_name(CFG_SchemaTable *table, String8 name)
|
||||
{
|
||||
MD_Node *result = &md_nil_node;
|
||||
{
|
||||
U64 hash = u64_hash_from_str8(name);
|
||||
U64 slot_idx = hash%table->slots_count;
|
||||
CFG_SchemaNode *node = 0;
|
||||
for(CFG_SchemaNode *n = table->slots[slot_idx]; n != 0; n = n->next)
|
||||
{
|
||||
if(str8_match(n->name, name, 0))
|
||||
{
|
||||
result = n->schema;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal MD_NodePtrList
|
||||
cfg_schemas_from_name(Arena *arena, CFG_SchemaTable *table, String8 name)
|
||||
{
|
||||
MD_NodePtrList result = {0};
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8List tasks = {0};
|
||||
String8Node seed_task = {0, name};
|
||||
str8_list_push_node(&tasks, &seed_task);
|
||||
for EachNode(task, String8Node, tasks.first)
|
||||
{
|
||||
MD_Node *schema = cfg_schema_from_name(table, task->string);
|
||||
if(!md_node_is_nil(schema))
|
||||
{
|
||||
md_node_ptr_list_push_front(arena, &result, schema);
|
||||
for MD_EachNode(tag, schema->first_tag)
|
||||
{
|
||||
if(str8_match(tag->string, str8_lit("inherit"), 0))
|
||||
{
|
||||
str8_list_push(scratch.arena, &tasks, tag->first->string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Config Reading Functions
|
||||
|
||||
//- rjf: context selection
|
||||
|
||||
internal void
|
||||
cfg_ctx_select(CFG_Ctx *ctx)
|
||||
{
|
||||
cfg_ctx = ctx;
|
||||
}
|
||||
|
||||
//- rjf: tree navigations
|
||||
|
||||
internal U64
|
||||
cfg_change_gen(void)
|
||||
{
|
||||
U64 result = 0;
|
||||
if(cfg_ctx != 0)
|
||||
{
|
||||
result = cfg_ctx->change_gen;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_root(void)
|
||||
{
|
||||
CFG_Node *result = &cfg_nil_node;
|
||||
if(cfg_ctx != 0)
|
||||
{
|
||||
result = cfg_ctx->root;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_from_id(CFG_ID id)
|
||||
{
|
||||
CFG_Node *result = &cfg_nil_node;
|
||||
if(id != 0 &&
|
||||
id == cfg_ctx->last_accessed_id &&
|
||||
id == cfg_ctx->last_accessed->id)
|
||||
{
|
||||
result = cfg_ctx->last_accessed;
|
||||
}
|
||||
else
|
||||
{
|
||||
U64 hash = u64_hash_from_str8(str8_struct(&id));
|
||||
U64 slot_idx = hash%cfg_ctx->id_slots_count;
|
||||
for(CFG_NodePtrNode *n = cfg_ctx->id_slots[slot_idx].first; n != 0; n = n->next)
|
||||
{
|
||||
if(n->v->id == id)
|
||||
{
|
||||
result = n->v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg_ctx->last_accessed_id = id;
|
||||
cfg_ctx->last_accessed = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_child_from_string(CFG_Node *parent, String8 string)
|
||||
{
|
||||
CFG_Node *child = &cfg_nil_node;
|
||||
if(string.size != 0)
|
||||
{
|
||||
for(CFG_Node *c = parent->first; c != &cfg_nil_node; c = c->next)
|
||||
{
|
||||
if(str8_match(c->string, string, 0))
|
||||
{
|
||||
child = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_child_from_string_or_parent(CFG_Node *parent, String8 string)
|
||||
{
|
||||
CFG_Node *result = cfg_node_child_from_string(parent, string);
|
||||
if(result == &cfg_nil_node)
|
||||
{
|
||||
result = parent;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_NodePtrList
|
||||
cfg_node_child_list_from_string(Arena *arena, CFG_Node *parent, String8 string)
|
||||
{
|
||||
CFG_NodePtrList result = {0};
|
||||
for(CFG_Node *child = parent->first; child != &cfg_nil_node; child = child->next)
|
||||
{
|
||||
if(str8_match(child->string, string, 0))
|
||||
{
|
||||
cfg_node_ptr_list_push(arena, &result, child);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_NodePtrList
|
||||
cfg_node_top_level_list_from_string(Arena *arena, String8 string)
|
||||
{
|
||||
CFG_NodePtrList result = {0};
|
||||
for(CFG_Node *bucket = cfg_ctx->root->first; bucket != &cfg_nil_node; bucket = bucket->next)
|
||||
{
|
||||
for(CFG_Node *tln = bucket->first; tln != &cfg_nil_node; tln = tln->next)
|
||||
{
|
||||
if(str8_match(tln->string, string, 0))
|
||||
{
|
||||
cfg_node_ptr_list_push(arena, &result, tln);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_NodeRec
|
||||
cfg_node_rec__depth_first(CFG_Node *root, CFG_Node *node)
|
||||
{
|
||||
CFG_NodeRec rec = {&cfg_nil_node};
|
||||
if(node->first != &cfg_nil_node)
|
||||
{
|
||||
rec.next = node->first;
|
||||
rec.push_count = 1;
|
||||
}
|
||||
else for(CFG_Node *p = node; p != root; p = p->parent, rec.pop_count += 1)
|
||||
{
|
||||
if(p->next != &cfg_nil_node)
|
||||
{
|
||||
rec.next = p->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
//- rjf: serialization
|
||||
|
||||
internal String8
|
||||
cfg_string_from_tree(Arena *arena, CFG_SchemaTable *schema_table, String8 root_path, CFG_Node *root)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8List strings = {0};
|
||||
{
|
||||
typedef struct NestTask NestTask;
|
||||
struct NestTask
|
||||
{
|
||||
NestTask *next;
|
||||
CFG_Node *cfg;
|
||||
MD_Node *schema;
|
||||
B32 is_simple;
|
||||
};
|
||||
NestTask *top_nest_task = 0;
|
||||
CFG_NodeRec rec = {0};
|
||||
for(CFG_Node *c = root; c != &cfg_nil_node; c = rec.next)
|
||||
{
|
||||
// rjf: look up parent's schemas
|
||||
MD_NodePtrList schemas = {0};
|
||||
if(top_nest_task != 0)
|
||||
{
|
||||
CFG_Node *parent = top_nest_task->cfg;
|
||||
schemas = cfg_schemas_from_name(scratch.arena, schema_table, parent->string);
|
||||
}
|
||||
|
||||
// rjf: look up child schema
|
||||
MD_Node *c_schema = &md_nil_node;
|
||||
for(MD_NodePtrNode *n = schemas.first; n != 0 && c_schema == &md_nil_node; n = n->next)
|
||||
{
|
||||
c_schema = md_child_from_string(n->v, c->string, 0);
|
||||
}
|
||||
|
||||
// rjf: push name of this node
|
||||
if(c->string.size != 0 || c->first == &cfg_nil_node)
|
||||
{
|
||||
// rjf: extract the textualized form for this string (we may need to escape / relativize)
|
||||
String8 c_serialized_string = c->string;
|
||||
{
|
||||
MD_Node *c_schema = &md_nil_node;
|
||||
if(top_nest_task != 0)
|
||||
{
|
||||
c_schema = top_nest_task->schema;
|
||||
}
|
||||
|
||||
// rjf: paths -> relativize
|
||||
if(!md_node_has_tag(c_schema->first, str8_lit("no_relativize"), 0))
|
||||
{
|
||||
if(str8_match(c_schema->first->string, str8_lit("path"), 0))
|
||||
{
|
||||
String8 path_absolute = c->string;
|
||||
String8 path_relative = path_relative_dst_from_absolute_dst_src(arena, path_absolute, root_path);
|
||||
c_serialized_string = path_relative;
|
||||
}
|
||||
else if(str8_match(c_schema->first->string, str8_lit("path_pt"), 0))
|
||||
{
|
||||
String8 value = c->string;
|
||||
String8TxtPtPair parts = str8_txt_pt_pair_from_string(value);
|
||||
String8 path_relative = path_relative_dst_from_absolute_dst_src(scratch.arena, parts.string, root_path);
|
||||
c_serialized_string = push_str8f(arena, "%S:%I64d:%I64d", path_relative, parts.pt.line, parts.pt.column);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: all strings -> escape
|
||||
c_serialized_string = escaped_from_raw_str8(arena, c_serialized_string);
|
||||
}
|
||||
|
||||
// rjf: generate all strings for this node's string
|
||||
String8List c_name_strings = {0};
|
||||
{
|
||||
B32 name_can_be_pushed_standalone = 0;
|
||||
{
|
||||
Temp temp = temp_begin(scratch.arena);
|
||||
MD_TokenizeResult c_name_tokenize = md_tokenize_from_text(temp.arena, c_serialized_string);
|
||||
name_can_be_pushed_standalone = (c_name_tokenize.tokens.count == 1 && c_name_tokenize.tokens.v[0].flags & (MD_TokenFlag_Identifier|
|
||||
MD_TokenFlag_Numeric|
|
||||
MD_TokenFlag_StringLiteral|
|
||||
MD_TokenFlag_Symbol));
|
||||
temp_end(temp);
|
||||
}
|
||||
if(name_can_be_pushed_standalone)
|
||||
{
|
||||
str8_list_push(scratch.arena, &c_name_strings, c_serialized_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
str8_list_push(scratch.arena, &c_name_strings, str8_lit("\""));
|
||||
str8_list_push(scratch.arena, &c_name_strings, c_serialized_string);
|
||||
str8_list_push(scratch.arena, &c_name_strings, str8_lit("\""));
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: if we're in a simple nesting task, then just break children by space
|
||||
if(top_nest_task != 0 && top_nest_task->is_simple)
|
||||
{
|
||||
str8_list_push(scratch.arena, &strings, str8_lit(" "));
|
||||
}
|
||||
|
||||
// rjf: join c's strings with main string list
|
||||
str8_list_concat_in_place(&strings, &c_name_strings);
|
||||
}
|
||||
|
||||
// rjf: grab next recursion
|
||||
rec = cfg_node_rec__depth_first(root, c);
|
||||
|
||||
// rjf: push a new nesting task before descending to children
|
||||
if(c->first != &cfg_nil_node)
|
||||
{
|
||||
B32 is_simple_children_list = 1;
|
||||
for(CFG_Node *child = c->first; child != &cfg_nil_node; child = child->next)
|
||||
{
|
||||
if(child->first != &cfg_nil_node && child != c->last)
|
||||
{
|
||||
is_simple_children_list = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
NestTask *task = push_array(scratch.arena, NestTask, 1);
|
||||
task->cfg = c;
|
||||
task->schema = c_schema;
|
||||
task->is_simple = is_simple_children_list;
|
||||
SLLStackPush(top_nest_task, task);
|
||||
}
|
||||
|
||||
// rjf: tree navigations -> encode hierarchy
|
||||
if(rec.push_count > 0)
|
||||
{
|
||||
if(top_nest_task->is_simple && c->string.size != 0)
|
||||
{
|
||||
str8_list_push(scratch.arena, &strings, str8_lit(":"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(c->string.size != 0)
|
||||
{
|
||||
str8_list_push(scratch.arena, &strings, str8_lit(":\n"));
|
||||
}
|
||||
str8_list_push(scratch.arena, &strings, str8_lit("{"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1, SLLStackPop(top_nest_task))
|
||||
{
|
||||
if(top_nest_task->is_simple)
|
||||
{
|
||||
if(top_nest_task->cfg->string.size == 0)
|
||||
{
|
||||
str8_list_push(scratch.arena, &strings, str8_lit(" }"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str8_list_push(scratch.arena, &strings, str8_lit("\n}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!top_nest_task || top_nest_task->is_simple == 0)
|
||||
{
|
||||
str8_list_push(scratch.arena, &strings, str8_lit("\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
String8 result_unindented = str8_list_join(scratch.arena, &strings, 0);
|
||||
String8 result = indented_from_string(arena, result_unindented);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Config Writing Functions
|
||||
|
||||
//- rjf: state creation / destroying
|
||||
|
||||
internal CFG_State *
|
||||
cfg_state_alloc(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
CFG_State *state = push_array(arena, CFG_State, 1);
|
||||
state->arena = arena;
|
||||
state->ctx.id_slots_count = 4096;
|
||||
state->ctx.id_slots = push_array(arena, CFG_NodePtrSlot, state->ctx.id_slots_count);
|
||||
state->ctx.root = cfg_node_alloc(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_state_release(CFG_State *state)
|
||||
{
|
||||
arena_release(state->arena);
|
||||
}
|
||||
|
||||
//- rjf: state -> ctx
|
||||
|
||||
internal CFG_Ctx *
|
||||
cfg_state_ctx(CFG_State *state)
|
||||
{
|
||||
CFG_Ctx *ctx = &state->ctx;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
//- rjf: string allocations
|
||||
|
||||
internal U64
|
||||
cfg_string_bucket_num_from_size(U64 size)
|
||||
{
|
||||
U64 bucket_num = 0;
|
||||
if(size > 0)
|
||||
{
|
||||
for EachElement(idx, cfg_string_bucket_chunk_sizes)
|
||||
{
|
||||
if(size <= cfg_string_bucket_chunk_sizes[idx])
|
||||
{
|
||||
bucket_num = idx+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bucket_num;
|
||||
}
|
||||
|
||||
internal String8
|
||||
cfg_string_alloc(CFG_State *state, String8 string)
|
||||
{
|
||||
//- rjf: allocate node
|
||||
CFG_StringChunkNode *node = 0;
|
||||
{
|
||||
U64 bucket_num = cfg_string_bucket_num_from_size(string.size);
|
||||
if(bucket_num == ArrayCount(cfg_string_bucket_chunk_sizes))
|
||||
{
|
||||
CFG_StringChunkNode *best_node = 0;
|
||||
CFG_StringChunkNode *best_node_prev = 0;
|
||||
U64 best_node_size = max_U64;
|
||||
{
|
||||
for(CFG_StringChunkNode *n = state->free_string_chunks[bucket_num-1], *prev = 0; n != 0; (prev = n, n = n->next))
|
||||
{
|
||||
if(n->size >= string.size && n->size < best_node_size)
|
||||
{
|
||||
best_node = n;
|
||||
best_node_prev = prev;
|
||||
best_node_size = n->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(best_node != 0)
|
||||
{
|
||||
node = best_node;
|
||||
if(best_node_prev)
|
||||
{
|
||||
best_node_prev->next = best_node->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->free_string_chunks[bucket_num-1] = best_node->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
U64 chunk_size = u64_up_to_pow2(string.size);
|
||||
node = (CFG_StringChunkNode *)push_array(state->arena, U8, chunk_size);
|
||||
}
|
||||
}
|
||||
else if(bucket_num != 0)
|
||||
{
|
||||
node = state->free_string_chunks[bucket_num-1];
|
||||
if(node != 0)
|
||||
{
|
||||
SLLStackPop(state->free_string_chunks[bucket_num-1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = (CFG_StringChunkNode *)push_array(state->arena, U8, cfg_string_bucket_chunk_sizes[bucket_num-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: fill node
|
||||
String8 result = {0};
|
||||
if(node != 0)
|
||||
{
|
||||
result.str = (U8 *)node;
|
||||
result.size = string.size;
|
||||
MemoryCopy(result.str, string.str, result.size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_string_release(CFG_State *state, String8 string)
|
||||
{
|
||||
U64 bucket_num = cfg_string_bucket_num_from_size(string.size);
|
||||
if(1 <= bucket_num && bucket_num <= ArrayCount(cfg_string_bucket_chunk_sizes))
|
||||
{
|
||||
U64 bucket_idx = bucket_num-1;
|
||||
CFG_StringChunkNode *node = (CFG_StringChunkNode *)string.str;
|
||||
SLLStackPush(state->free_string_chunks[bucket_idx], node);
|
||||
node->size = u64_up_to_pow2(string.size);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: tree building
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_alloc(CFG_State *state)
|
||||
{
|
||||
state->ctx.change_gen += 1;
|
||||
|
||||
// rjf: allocate
|
||||
CFG_Node *result = state->free;
|
||||
{
|
||||
if(result)
|
||||
{
|
||||
SLLStackPop(state->free);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = push_array_no_zero(state->arena, CFG_Node, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: generate ID & fill
|
||||
state->id_gen += 1;
|
||||
MemoryZeroStruct(result);
|
||||
result->first = result->last = result->next = result->prev = result->parent = &cfg_nil_node;
|
||||
result->id = state->id_gen;
|
||||
|
||||
// rjf: store to ID -> cfg map
|
||||
{
|
||||
CFG_NodePtrNode *cfg_id_node = state->free_id_node;
|
||||
if(cfg_id_node != 0)
|
||||
{
|
||||
SLLStackPop(state->free_id_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
cfg_id_node = push_array(state->arena, CFG_NodePtrNode, 1);
|
||||
}
|
||||
U64 hash = u64_hash_from_str8(str8_struct(&result->id));
|
||||
U64 slot_idx = hash%state->ctx.id_slots_count;
|
||||
DLLPushBack(state->ctx.id_slots[slot_idx].first, state->ctx.id_slots[slot_idx].last, cfg_id_node);
|
||||
cfg_id_node->v = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_node_release(CFG_State *state, CFG_Node *node)
|
||||
{
|
||||
state->ctx.change_gen += 1;
|
||||
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
// rjf: unhook from context
|
||||
cfg_node_unhook(state, node->parent, node);
|
||||
|
||||
// rjf: gather root & all descendants
|
||||
CFG_NodePtrList nodes = {0};
|
||||
for(CFG_Node *c = node; c != &cfg_nil_node; c = cfg_node_rec__depth_first(node, c).next)
|
||||
{
|
||||
cfg_node_ptr_list_push(scratch.arena, &nodes, c);
|
||||
}
|
||||
|
||||
// rjf: release all nodes
|
||||
for(CFG_NodePtrNode *n = nodes.first; n != 0; n = n->next)
|
||||
{
|
||||
CFG_Node *c = n->v;
|
||||
cfg_string_release(state, c->string);
|
||||
SLLStackPush(state->free, c);
|
||||
c->first = c->last = c->prev = c->parent = 0;
|
||||
c->id = 0;
|
||||
c->string = str8_zero();
|
||||
U64 hash = u64_hash_from_str8(str8_struct(&c->id));
|
||||
U64 slot_idx = hash%state->ctx.id_slots_count;
|
||||
for(CFG_NodePtrNode *n = state->ctx.id_slots[slot_idx].first; n != 0; n = n->next)
|
||||
{
|
||||
if(n->v == c)
|
||||
{
|
||||
DLLRemove(state->ctx.id_slots[slot_idx].first, state->ctx.id_slots[slot_idx].last, n);
|
||||
SLLStackPush(state->free_id_node, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_node_release_all_children(CFG_State *state, CFG_Node *node)
|
||||
{
|
||||
for(CFG_Node *child = node->first, *next = &cfg_nil_node; child != &cfg_nil_node; child = next)
|
||||
{
|
||||
next = child->next;
|
||||
cfg_node_release(state, child);
|
||||
}
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_new(CFG_State *state, CFG_Node *parent, String8 string)
|
||||
{
|
||||
CFG_Node *node = cfg_node_alloc(state);
|
||||
cfg_node_insert_child(state, parent, parent->last, node);
|
||||
cfg_node_equip_string(state, node, string);
|
||||
return node;
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_newf(CFG_State *state, CFG_Node *parent, char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
CFG_Node *result = cfg_node_new(state, parent, string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_new_replace(CFG_State *state, CFG_Node *parent, String8 string)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
string = push_str8_copy(scratch.arena, string);
|
||||
for(CFG_Node *child = parent->first->next, *next = &cfg_nil_node; child != &cfg_nil_node; child = next)
|
||||
{
|
||||
next = child->next;
|
||||
cfg_node_release(state, child);
|
||||
}
|
||||
if(parent->first == &cfg_nil_node)
|
||||
{
|
||||
cfg_node_new(state, parent, str8_zero());
|
||||
}
|
||||
CFG_Node *child = parent->first;
|
||||
cfg_node_equip_string(state, child, string);
|
||||
scratch_end(scratch);
|
||||
return child;
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_new_replacef(CFG_State *state, CFG_Node *parent, char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
CFG_Node *result = cfg_node_new_replace(state, parent, string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_deep_copy(CFG_State *state, CFG_Node *src_root)
|
||||
{
|
||||
CFG_NodeRec rec = {0};
|
||||
CFG_Node *dst_root = &cfg_nil_node;
|
||||
CFG_Node *dst_parent = &cfg_nil_node;
|
||||
for(CFG_Node *src = src_root; src != &cfg_nil_node; src = rec.next)
|
||||
{
|
||||
CFG_Node *dst = cfg_node_new(state, dst_parent, src->string);
|
||||
if(dst_root == &cfg_nil_node)
|
||||
{
|
||||
dst_root = dst;
|
||||
}
|
||||
rec = cfg_node_rec__depth_first(src_root, src);
|
||||
if(rec.push_count > 0)
|
||||
{
|
||||
dst_parent = dst;
|
||||
}
|
||||
else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
|
||||
{
|
||||
dst_parent = dst_parent->parent;
|
||||
}
|
||||
}
|
||||
return dst_root;
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_node_equip_string(CFG_State *state, CFG_Node *node, String8 string)
|
||||
{
|
||||
cfg_string_release(state, node->string);
|
||||
node->string = cfg_string_alloc(state, string);
|
||||
state->ctx.change_gen += 1;
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_node_equip_stringf(CFG_State *state, CFG_Node *node, char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
cfg_node_equip_string(state, node, string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_node_insert_child(CFG_State *state, CFG_Node *parent, CFG_Node *prev_child, CFG_Node *new_child)
|
||||
{
|
||||
if(parent != &cfg_nil_node)
|
||||
{
|
||||
if(new_child->parent != &cfg_nil_node)
|
||||
{
|
||||
cfg_node_unhook(state, new_child->parent, new_child);
|
||||
}
|
||||
DLLInsert_NPZ(&cfg_nil_node, parent->first, parent->last, prev_child, new_child, next, prev);
|
||||
new_child->parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
cfg_node_unhook(CFG_State *state, CFG_Node *parent, CFG_Node *child)
|
||||
{
|
||||
if(child != &cfg_nil_node && parent == child->parent && parent != &cfg_nil_node)
|
||||
{
|
||||
DLLRemove_NPZ(&cfg_nil_node, parent->first, parent->last, child, next, prev);
|
||||
child->parent = &cfg_nil_node;
|
||||
}
|
||||
}
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_node_child_from_string_or_alloc(CFG_State *state, CFG_Node *parent, String8 string)
|
||||
{
|
||||
CFG_Node *node = cfg_node_child_from_string(parent, string);
|
||||
if(node == &cfg_nil_node)
|
||||
{
|
||||
node = cfg_node_new(state, parent, string);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
//- rjf: deserialization
|
||||
|
||||
internal CFG_NodePtrList
|
||||
cfg_node_ptr_list_from_string(Arena *arena, CFG_State *state, CFG_SchemaTable *schema_table, String8 root_path, String8 string)
|
||||
{
|
||||
CFG_NodePtrList result = {0};
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
//- rjf: parse the string as metadesk
|
||||
MD_Node *root = md_tree_from_string(scratch.arena, string);
|
||||
|
||||
//- rjf: iterate the top-level metadesk trees, generate new cfg trees for each
|
||||
for MD_EachNode(tln, root->first)
|
||||
{
|
||||
CFG_Node *dst_root_n = &cfg_nil_node;
|
||||
CFG_Node *dst_active_parent_n = &cfg_nil_node;
|
||||
MD_NodeRec rec = {0};
|
||||
for(MD_Node *src_n = tln; !md_node_is_nil(src_n); src_n = rec.next)
|
||||
{
|
||||
// rjf: lookup schema for this string
|
||||
MD_Node *schema = &md_nil_node;
|
||||
{
|
||||
MD_NodePtrList schemas = cfg_schemas_from_name(scratch.arena, schema_table, dst_active_parent_n->parent->string);
|
||||
for(MD_NodePtrNode *n = schemas.first; n != 0 && schema == &md_nil_node; n = n->next)
|
||||
{
|
||||
schema = md_child_from_string(n->v, dst_active_parent_n->string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: extract & transform metadesk node's string (it is raw textual data, so we need to
|
||||
// go escaped -> raw, and derelativize paths)
|
||||
String8 dst_n_string = {0};
|
||||
{
|
||||
String8 src_n_string = src_n->string;
|
||||
String8 src_n_string__raw = raw_from_escaped_str8(scratch.arena, src_n_string);
|
||||
if(!md_node_has_tag(schema->first, str8_lit("no_relativize"), 0))
|
||||
{
|
||||
if(str8_match(schema->first->string, str8_lit("path"), 0))
|
||||
{
|
||||
src_n_string__raw = path_absolute_dst_from_relative_dst_src(scratch.arena, src_n_string__raw, root_path);
|
||||
}
|
||||
else if(str8_match(schema->first->string, str8_lit("path_pt"), 0))
|
||||
{
|
||||
String8TxtPtPair parts = str8_txt_pt_pair_from_string(src_n_string__raw);
|
||||
src_n_string__raw = push_str8f(scratch.arena, "%S:%I64d:%I64d", path_absolute_dst_from_relative_dst_src(scratch.arena, parts.string, root_path), parts.pt.line, parts.pt.column);
|
||||
}
|
||||
}
|
||||
dst_n_string = src_n_string__raw;
|
||||
}
|
||||
|
||||
// rjf: allocate, fill, & insert new cfg for this metadesk node
|
||||
CFG_Node *dst_n = cfg_node_alloc(state);
|
||||
cfg_node_equip_string(state, dst_n, dst_n_string);
|
||||
if(dst_active_parent_n != &cfg_nil_node)
|
||||
{
|
||||
cfg_node_insert_child(state, dst_active_parent_n, dst_active_parent_n->last, dst_n);
|
||||
}
|
||||
|
||||
// rjf: recurse
|
||||
rec = md_node_rec_depth_first_pre(src_n, tln);
|
||||
if(dst_active_parent_n == &cfg_nil_node)
|
||||
{
|
||||
dst_root_n = dst_n;
|
||||
}
|
||||
if(rec.push_count > 0)
|
||||
{
|
||||
dst_active_parent_n = dst_n;
|
||||
}
|
||||
else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
|
||||
{
|
||||
dst_active_parent_n = dst_active_parent_n->parent;
|
||||
}
|
||||
}
|
||||
cfg_node_ptr_list_push(arena, &result, dst_root_n);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CONFIG_CORE_H
|
||||
#define CONFIG_CORE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: IDs
|
||||
|
||||
typedef U64 CFG_ID;
|
||||
|
||||
typedef struct CFG_IDNode CFG_IDNode;
|
||||
struct CFG_IDNode
|
||||
{
|
||||
CFG_IDNode *next;
|
||||
CFG_ID v;
|
||||
};
|
||||
|
||||
typedef struct CFG_IDList CFG_IDList;
|
||||
struct CFG_IDList
|
||||
{
|
||||
CFG_IDNode *first;
|
||||
CFG_IDNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Tree Types
|
||||
|
||||
typedef struct CFG_Node CFG_Node;
|
||||
struct CFG_Node
|
||||
{
|
||||
CFG_Node *first;
|
||||
CFG_Node *last;
|
||||
CFG_Node *next;
|
||||
CFG_Node *prev;
|
||||
CFG_Node *parent;
|
||||
CFG_ID id;
|
||||
String8 string;
|
||||
};
|
||||
|
||||
typedef struct CFG_NodePtrNode CFG_NodePtrNode;
|
||||
struct CFG_NodePtrNode
|
||||
{
|
||||
CFG_NodePtrNode *next;
|
||||
CFG_NodePtrNode *prev;
|
||||
CFG_Node *v;
|
||||
};
|
||||
|
||||
typedef struct CFG_NodePtrSlot CFG_NodePtrSlot;
|
||||
struct CFG_NodePtrSlot
|
||||
{
|
||||
CFG_NodePtrNode *first;
|
||||
CFG_NodePtrNode *last;
|
||||
};
|
||||
|
||||
typedef struct CFG_NodePtrList CFG_NodePtrList;
|
||||
struct CFG_NodePtrList
|
||||
{
|
||||
CFG_NodePtrNode *first;
|
||||
CFG_NodePtrNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct CFG_NodePtrArray CFG_NodePtrArray;
|
||||
struct CFG_NodePtrArray
|
||||
{
|
||||
CFG_Node **v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct CFG_NodeRec CFG_NodeRec;
|
||||
struct CFG_NodeRec
|
||||
{
|
||||
CFG_Node *next;
|
||||
S32 push_count;
|
||||
S32 pop_count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Allocator
|
||||
|
||||
read_only global U64 cfg_string_bucket_chunk_sizes[] =
|
||||
{
|
||||
16,
|
||||
64,
|
||||
256,
|
||||
1024,
|
||||
4096,
|
||||
16384,
|
||||
65536,
|
||||
0xffffffffffffffffull,
|
||||
};
|
||||
|
||||
typedef struct CFG_StringChunkNode CFG_StringChunkNode;
|
||||
struct CFG_StringChunkNode
|
||||
{
|
||||
CFG_StringChunkNode *next;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Config State Bundles
|
||||
|
||||
typedef struct CFG_Ctx CFG_Ctx;
|
||||
struct CFG_Ctx
|
||||
{
|
||||
CFG_Node *root;
|
||||
U64 id_slots_count;
|
||||
CFG_NodePtrSlot *id_slots;
|
||||
U64 change_gen;
|
||||
CFG_ID last_accessed_id;
|
||||
CFG_Node *last_accessed;
|
||||
};
|
||||
|
||||
typedef struct CFG_State CFG_State;
|
||||
struct CFG_State
|
||||
{
|
||||
Arena *arena;
|
||||
CFG_Node *free;
|
||||
CFG_NodePtrNode *free_id_node;
|
||||
CFG_StringChunkNode *free_string_chunks[ArrayCount(cfg_string_bucket_chunk_sizes)];
|
||||
U64 id_gen;
|
||||
CFG_Ctx ctx;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Schema Table
|
||||
|
||||
typedef struct CFG_SchemaNode CFG_SchemaNode;
|
||||
struct CFG_SchemaNode
|
||||
{
|
||||
CFG_SchemaNode *next;
|
||||
String8 name;
|
||||
MD_Node *schema;
|
||||
};
|
||||
|
||||
typedef struct CFG_SchemaTable CFG_SchemaTable;
|
||||
struct CFG_SchemaTable
|
||||
{
|
||||
CFG_SchemaNode **slots;
|
||||
U64 slots_count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
read_only global CFG_Node cfg_nil_node =
|
||||
{
|
||||
&cfg_nil_node,
|
||||
&cfg_nil_node,
|
||||
&cfg_nil_node,
|
||||
&cfg_nil_node,
|
||||
&cfg_nil_node,
|
||||
};
|
||||
|
||||
thread_static CFG_Ctx *cfg_ctx = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: ID Functions
|
||||
|
||||
internal void cfg_id_list_push(Arena *arena, CFG_IDList *list, CFG_ID id);
|
||||
internal CFG_IDList cfg_id_list_copy(Arena *arena, CFG_IDList *src);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Node Pointer Data Structure Functions
|
||||
|
||||
internal void cfg_node_ptr_list_push(Arena *arena, CFG_NodePtrList *list, CFG_Node *node);
|
||||
internal void cfg_node_ptr_list_push_front(Arena *arena, CFG_NodePtrList *list, CFG_Node *node);
|
||||
#define cfg_node_ptr_list_first(list) ((list)->count ? (list)->first->v : &cfg_nil_node)
|
||||
#define cfg_node_ptr_list_last(list) ((list)->count ? (list)->last->v : &cfg_nil_node)
|
||||
internal CFG_NodePtrArray cfg_node_ptr_array_from_list(Arena *arena, CFG_NodePtrList *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Schema Data Structure Functions
|
||||
|
||||
internal void cfg_schema_table_insert(Arena *arena, CFG_SchemaTable *table, String8 name, MD_Node *schema);
|
||||
internal MD_Node *cfg_schema_from_name(CFG_SchemaTable *table, String8 name);
|
||||
internal MD_NodePtrList cfg_schemas_from_name(Arena *arena, CFG_SchemaTable *table, String8 name);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Config Reading Functions
|
||||
|
||||
//- rjf: context selection
|
||||
internal void cfg_ctx_select(CFG_Ctx *ctx);
|
||||
|
||||
//- rjf: tree navigations
|
||||
internal U64 cfg_change_gen(void);
|
||||
internal CFG_Node *cfg_node_root(void);
|
||||
internal CFG_Node *cfg_node_from_id(CFG_ID id);
|
||||
internal CFG_Node *cfg_node_child_from_string(CFG_Node *parent, String8 string);
|
||||
internal CFG_Node *cfg_node_child_from_string_or_parent(CFG_Node *parent, String8 string);
|
||||
internal CFG_NodePtrList cfg_node_child_list_from_string(Arena *arena, CFG_Node *parent, String8 string);
|
||||
internal CFG_NodePtrList cfg_node_top_level_list_from_string(Arena *arena, String8 string);
|
||||
internal CFG_NodeRec cfg_node_rec__depth_first(CFG_Node *root, CFG_Node *node);
|
||||
|
||||
//- rjf: serialization
|
||||
internal String8 cfg_string_from_tree(Arena *arena, CFG_SchemaTable *schema_table, String8 root_path, CFG_Node *root);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Config Writing Functions
|
||||
|
||||
//- rjf: state creation / destroying
|
||||
internal CFG_State *cfg_state_alloc(void);
|
||||
internal void cfg_state_release(CFG_State *state);
|
||||
|
||||
//- rjf: state -> ctx
|
||||
internal CFG_Ctx *cfg_state_ctx(CFG_State *state);
|
||||
|
||||
//- rjf: string allocations
|
||||
internal U64 cfg_string_bucket_num_from_size(U64 size);
|
||||
internal String8 cfg_string_alloc(CFG_State *state, String8 string);
|
||||
internal void cfg_string_release(CFG_State *state, String8 string);
|
||||
|
||||
//- rjf: tree building
|
||||
internal CFG_Node *cfg_node_alloc(CFG_State *state);
|
||||
internal void cfg_node_release(CFG_State *state, CFG_Node *node);
|
||||
internal void cfg_node_release_all_children(CFG_State *state, CFG_Node *node);
|
||||
internal CFG_Node *cfg_node_new(CFG_State *state, CFG_Node *parent, String8 string);
|
||||
internal CFG_Node *cfg_node_newf(CFG_State *state, CFG_Node *parent, char *fmt, ...);
|
||||
internal CFG_Node *cfg_node_new_replace(CFG_State *state, CFG_Node *parent, String8 string);
|
||||
internal CFG_Node *cfg_node_new_replacef(CFG_State *state, CFG_Node *parent, char *fmt, ...);
|
||||
internal CFG_Node *cfg_node_deep_copy(CFG_State *state, CFG_Node *src_root);
|
||||
internal void cfg_node_equip_string(CFG_State *state, CFG_Node *node, String8 string);
|
||||
internal void cfg_node_equip_stringf(CFG_State *state, CFG_Node *node, char *fmt, ...);
|
||||
internal void cfg_node_insert_child(CFG_State *state, CFG_Node *parent, CFG_Node *prev_child, CFG_Node *new_child);
|
||||
internal void cfg_node_unhook(CFG_State *state, CFG_Node *parent, CFG_Node *child);
|
||||
internal CFG_Node *cfg_node_child_from_string_or_alloc(CFG_State *state, CFG_Node *parent, String8 string);
|
||||
|
||||
//- rjf: deserialization
|
||||
internal CFG_NodePtrList cfg_node_ptr_list_from_string(Arena *arena, CFG_State *state, CFG_SchemaTable *schema_table, String8 root_path, String8 string);
|
||||
|
||||
#endif // CONFIG_CORE_H
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#include "config_core.c"
|
||||
#include "config_panels.c"
|
||||
#if defined(OS_GFX_H)
|
||||
# include "config_bindings.c"
|
||||
#endif
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CONFIG_INC_H
|
||||
#define CONFIG_INC_H
|
||||
|
||||
#include "config_core.h"
|
||||
#include "config_panels.h"
|
||||
#if defined(OS_GFX_H)
|
||||
# include "config_bindings.h"
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_INC_H
|
||||
@@ -1,217 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal CFG_Node *
|
||||
cfg_window_from_cfg(CFG_Node *cfg)
|
||||
{
|
||||
CFG_Node *result = &cfg_nil_node;
|
||||
for(CFG_Node *c = cfg; c != &cfg_nil_node; c = c->parent)
|
||||
{
|
||||
if(c->parent->parent == cfg_node_root() && str8_match(c->string, str8_lit("window"), 0))
|
||||
{
|
||||
result = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CFG_PanelTree
|
||||
cfg_panel_tree_from_cfg(Arena *arena, CFG_Node *cfg_root)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
CFG_Node *wcfg = cfg_window_from_cfg(cfg_root);
|
||||
CFG_Node *src_root = cfg_node_child_from_string(wcfg, str8_lit("panels"));
|
||||
CFG_PanelNode *dst_root = &cfg_nil_panel_node;
|
||||
CFG_PanelNode *dst_focused = &cfg_nil_panel_node;
|
||||
{
|
||||
Axis2 active_split_axis = cfg_node_child_from_string(wcfg, str8_lit("split_x")) != &cfg_nil_node ? Axis2_X : Axis2_Y;
|
||||
CFG_NodeRec rec = {0};
|
||||
CFG_PanelNode *dst_active_parent = &cfg_nil_panel_node;
|
||||
for(CFG_Node *src = src_root; src != &cfg_nil_node; src = rec.next)
|
||||
{
|
||||
// rjf: build a panel node
|
||||
CFG_PanelNode *dst = push_array(arena, CFG_PanelNode, 1);
|
||||
MemoryCopyStruct(dst, &cfg_nil_panel_node);
|
||||
dst->parent = dst_active_parent;
|
||||
if(dst_active_parent != &cfg_nil_panel_node)
|
||||
{
|
||||
DLLPushBack_NPZ(&cfg_nil_panel_node, dst_active_parent->first, dst_active_parent->last, dst, next, prev);
|
||||
dst_active_parent->child_count += 1;
|
||||
}
|
||||
if(dst_root == &cfg_nil_panel_node)
|
||||
{
|
||||
dst_root = dst;
|
||||
}
|
||||
|
||||
// rjf: extract cfg info
|
||||
B32 panel_has_children = 0;
|
||||
dst->cfg = src;
|
||||
dst->pct_of_parent = (src == src_root ? 1.f : (F32)f64_from_str8(src->string));
|
||||
dst->tab_side = (cfg_node_child_from_string(src, str8_lit("tabs_on_bottom")) != &cfg_nil_node ? Side_Max : Side_Min);
|
||||
dst->split_axis = active_split_axis;
|
||||
for(CFG_Node *src_child = src->first; src_child != &cfg_nil_node; src_child = src_child->next)
|
||||
{
|
||||
MD_TokenizeResult tokenize = md_tokenize_from_text(scratch.arena, src_child->string);
|
||||
if(tokenize.tokens.count == 1 && tokenize.tokens.v[0].flags & MD_TokenFlag_Numeric)
|
||||
{
|
||||
panel_has_children = 1;
|
||||
}
|
||||
else if(str8_match(src_child->string, str8_lit("tabs_on_bottom"), 0))
|
||||
{
|
||||
// NOTE(rjf): skip - this is a panel option.
|
||||
}
|
||||
else if(str8_match(src_child->string, str8_lit("selected"), 0))
|
||||
{
|
||||
dst_focused = dst;
|
||||
}
|
||||
else if(tokenize.tokens.count == 1 && tokenize.tokens.v[0].flags & MD_TokenFlag_Identifier)
|
||||
{
|
||||
cfg_node_ptr_list_push(arena, &dst->tabs, src_child);
|
||||
if(cfg_node_child_from_string(src_child, str8_lit("selected")) != &cfg_nil_node)
|
||||
{
|
||||
dst->selected_tab = src_child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: recurse
|
||||
rec = cfg_node_rec__depth_first(src_root, src);
|
||||
if(!panel_has_children)
|
||||
{
|
||||
MemoryZeroStruct(&rec);
|
||||
rec.next = &cfg_nil_node;
|
||||
for(CFG_Node *p = src; p != src_root && p != &cfg_nil_node; p = p->parent, rec.pop_count += 1)
|
||||
{
|
||||
if(p->next != &cfg_nil_node)
|
||||
{
|
||||
rec.next = p->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(rec.push_count > 0)
|
||||
{
|
||||
dst_active_parent = dst;
|
||||
active_split_axis = axis2_flip(active_split_axis);
|
||||
}
|
||||
else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
|
||||
{
|
||||
dst_active_parent = dst_active_parent->parent;
|
||||
active_split_axis = axis2_flip(active_split_axis);
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
CFG_PanelTree tree = {dst_root, dst_focused};
|
||||
return tree;
|
||||
}
|
||||
|
||||
internal CFG_PanelNodeRec
|
||||
cfg_panel_node_rec__depth_first(CFG_PanelNode *root, CFG_PanelNode *panel, U64 sib_off, U64 child_off)
|
||||
{
|
||||
CFG_PanelNodeRec rec = {&cfg_nil_panel_node};
|
||||
if(*MemberFromOffset(CFG_PanelNode **, panel, child_off) != &cfg_nil_panel_node)
|
||||
{
|
||||
rec.next = *MemberFromOffset(CFG_PanelNode **, panel, child_off);
|
||||
rec.push_count += 1;
|
||||
}
|
||||
else for(CFG_PanelNode *p = panel; p != &cfg_nil_panel_node && p != root; p = p->parent, rec.pop_count += 1)
|
||||
{
|
||||
if(*MemberFromOffset(CFG_PanelNode **, p, sib_off) != &cfg_nil_panel_node)
|
||||
{
|
||||
rec.next = *MemberFromOffset(CFG_PanelNode **, p, sib_off);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
internal CFG_PanelNode *
|
||||
cfg_panel_node_from_tree_cfg(CFG_PanelNode *root, CFG_Node *cfg)
|
||||
{
|
||||
CFG_PanelNode *result = &cfg_nil_panel_node;
|
||||
for(CFG_PanelNode *p = root;
|
||||
p != &cfg_nil_panel_node;
|
||||
p = cfg_panel_node_rec__depth_first_pre(root, p).next)
|
||||
{
|
||||
if(p->cfg == cfg)
|
||||
{
|
||||
result = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal Rng2F32
|
||||
cfg_target_rect_from_panel_node_child(Rng2F32 parent_rect, CFG_PanelNode *parent, CFG_PanelNode *panel)
|
||||
{
|
||||
Rng2F32 rect = parent_rect;
|
||||
if(parent != &cfg_nil_panel_node)
|
||||
{
|
||||
Vec2F32 parent_rect_size = dim_2f32(parent_rect);
|
||||
Axis2 axis = parent->split_axis;
|
||||
rect.p1.v[axis] = rect.p0.v[axis];
|
||||
for(CFG_PanelNode *child = parent->first; child != &cfg_nil_panel_node; child = child->next)
|
||||
{
|
||||
rect.p1.v[axis] += parent_rect_size.v[axis] * child->pct_of_parent;
|
||||
if(child == panel)
|
||||
{
|
||||
break;
|
||||
}
|
||||
rect.p0.v[axis] = rect.p1.v[axis];
|
||||
}
|
||||
//rect.p0.v[axis] += parent_rect_size.v[axis] * panel->off_pct_of_parent.v[axis];
|
||||
//rect.p0.v[axis2_flip(axis)] += parent_rect_size.v[axis2_flip(axis)] * panel->off_pct_of_parent.v[axis2_flip(axis)];
|
||||
}
|
||||
rect.x0 = round_f32(rect.x0);
|
||||
rect.x1 = round_f32(rect.x1);
|
||||
rect.y0 = round_f32(rect.y0);
|
||||
rect.y1 = round_f32(rect.y1);
|
||||
return rect;
|
||||
}
|
||||
|
||||
internal Rng2F32
|
||||
cfg_target_rect_from_panel_node(Rng2F32 root_rect, CFG_PanelNode *root, CFG_PanelNode *panel)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
// rjf: count ancestors
|
||||
U64 ancestor_count = 0;
|
||||
for(CFG_PanelNode *p = panel->parent; p != &cfg_nil_panel_node; p = p->parent)
|
||||
{
|
||||
ancestor_count += 1;
|
||||
}
|
||||
|
||||
// rjf: gather ancestors
|
||||
CFG_PanelNode **ancestors = push_array(scratch.arena, CFG_PanelNode *, ancestor_count);
|
||||
{
|
||||
U64 ancestor_idx = 0;
|
||||
for(CFG_PanelNode *p = panel->parent; p != &cfg_nil_panel_node; p = p->parent)
|
||||
{
|
||||
ancestors[ancestor_idx] = p;
|
||||
ancestor_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: go from highest ancestor => panel and calculate rect
|
||||
Rng2F32 parent_rect = root_rect;
|
||||
for(S64 ancestor_idx = (S64)ancestor_count-1;
|
||||
0 <= ancestor_idx && ancestor_idx < ancestor_count;
|
||||
ancestor_idx -= 1)
|
||||
{
|
||||
CFG_PanelNode *ancestor = ancestors[ancestor_idx];
|
||||
CFG_PanelNode *parent = ancestor->parent;
|
||||
if(parent != &cfg_nil_panel_node)
|
||||
{
|
||||
parent_rect = cfg_target_rect_from_panel_node_child(parent_rect, parent, ancestor);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: calculate final rect
|
||||
Rng2F32 rect = cfg_target_rect_from_panel_node_child(parent_rect, panel->parent, panel);
|
||||
|
||||
scratch_end(scratch);
|
||||
return rect;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CONFIG_PANELS_H
|
||||
#define CONFIG_PANELS_H
|
||||
|
||||
typedef struct CFG_PanelNode CFG_PanelNode;
|
||||
struct CFG_PanelNode
|
||||
{
|
||||
// rjf: links data
|
||||
CFG_PanelNode *first;
|
||||
CFG_PanelNode *last;
|
||||
CFG_PanelNode *next;
|
||||
CFG_PanelNode *prev;
|
||||
CFG_PanelNode *parent;
|
||||
U64 child_count;
|
||||
CFG_Node *cfg;
|
||||
|
||||
// rjf: split data
|
||||
Axis2 split_axis;
|
||||
F32 pct_of_parent;
|
||||
|
||||
// rjf: tab params
|
||||
Side tab_side;
|
||||
|
||||
// rjf: which tabs are attached
|
||||
CFG_NodePtrList tabs;
|
||||
CFG_Node *selected_tab;
|
||||
};
|
||||
|
||||
typedef struct CFG_PanelTree CFG_PanelTree;
|
||||
struct CFG_PanelTree
|
||||
{
|
||||
CFG_PanelNode *root;
|
||||
CFG_PanelNode *focused;
|
||||
};
|
||||
|
||||
typedef struct CFG_PanelNodeRec CFG_PanelNodeRec;
|
||||
struct CFG_PanelNodeRec
|
||||
{
|
||||
CFG_PanelNode *next;
|
||||
S32 push_count;
|
||||
S32 pop_count;
|
||||
};
|
||||
|
||||
read_only global CFG_PanelNode cfg_nil_panel_node =
|
||||
{
|
||||
&cfg_nil_panel_node,
|
||||
&cfg_nil_panel_node,
|
||||
&cfg_nil_panel_node,
|
||||
&cfg_nil_panel_node,
|
||||
&cfg_nil_panel_node,
|
||||
0,
|
||||
&cfg_nil_node,
|
||||
.selected_tab = &cfg_nil_node,
|
||||
};
|
||||
|
||||
internal CFG_Node *cfg_window_from_cfg(CFG_Node *cfg);
|
||||
internal CFG_PanelTree cfg_panel_tree_from_cfg(Arena *arena, CFG_Node *cfg_root);
|
||||
internal CFG_PanelNodeRec cfg_panel_node_rec__depth_first(CFG_PanelNode *root, CFG_PanelNode *panel, U64 sib_off, U64 child_off);
|
||||
#define cfg_panel_node_rec__depth_first_pre(root, p) cfg_panel_node_rec__depth_first((root), (p), OffsetOf(CFG_PanelNode, next), OffsetOf(CFG_PanelNode, first))
|
||||
#define cfg_panel_node_rec__depth_first_pre_rev(root, p) cfg_panel_node_rec__depth_first((root), (p), OffsetOf(CFG_PanelNode, prev), OffsetOf(CFG_PanelNode, last))
|
||||
internal CFG_PanelNode *cfg_panel_node_from_tree_cfg(CFG_PanelNode *root, CFG_Node *cfg);
|
||||
internal Rng2F32 cfg_target_rect_from_panel_node_child(Rng2F32 parent_rect, CFG_PanelNode *parent, CFG_PanelNode *panel);
|
||||
internal Rng2F32 cfg_target_rect_from_panel_node(Rng2F32 root_rect, CFG_PanelNode *root, CFG_PanelNode *panel);
|
||||
|
||||
#endif // CONFIG_PANELS_H
|
||||
@@ -1,522 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#undef LAYER_COLOR
|
||||
#define LAYER_COLOR 0x684123ff
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal C_ID
|
||||
c_id_make(U64 u64_0, U64 u64_1)
|
||||
{
|
||||
C_ID id;
|
||||
id.u128[0].u64[0] = u64_0;
|
||||
id.u128[0].u64[1] = u64_1;
|
||||
return id;
|
||||
}
|
||||
|
||||
internal B32
|
||||
c_id_match(C_ID a, C_ID b)
|
||||
{
|
||||
B32 result = MemoryMatchStruct(&a, &b);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal C_Key
|
||||
c_key_make(C_Root root, C_ID id)
|
||||
{
|
||||
C_Key key = {root, 0, id};
|
||||
return key;
|
||||
}
|
||||
|
||||
internal B32
|
||||
c_key_match(C_Key a, C_Key b)
|
||||
{
|
||||
return (MemoryMatchStruct(&a.root, &b.root) && c_id_match(a.id, b.id));
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void
|
||||
c_init(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
c_shared = push_array(arena, C_Shared, 1);
|
||||
c_shared->arena = arena;
|
||||
c_shared->blob_slots_count = 16384;
|
||||
c_shared->blob_stripes_count = Min(c_shared->blob_slots_count, os_get_system_info()->logical_processor_count);
|
||||
c_shared->blob_slots = push_array(arena, C_BlobSlot, c_shared->blob_slots_count);
|
||||
c_shared->blob_stripes = push_array(arena, C_Stripe, c_shared->blob_stripes_count);
|
||||
c_shared->blob_stripes_free_nodes = push_array(arena, C_BlobNode *, c_shared->blob_stripes_count);
|
||||
for(U64 idx = 0; idx < c_shared->blob_stripes_count; idx += 1)
|
||||
{
|
||||
C_Stripe *stripe = &c_shared->blob_stripes[idx];
|
||||
stripe->arena = arena_alloc();
|
||||
stripe->rw_mutex = rw_mutex_alloc();
|
||||
stripe->cv = cond_var_alloc();
|
||||
}
|
||||
c_shared->key_slots_count = 4096;
|
||||
c_shared->key_stripes_count = Min(c_shared->key_slots_count, os_get_system_info()->logical_processor_count);
|
||||
c_shared->key_slots = push_array(arena, C_KeySlot, c_shared->key_slots_count);
|
||||
c_shared->key_stripes = push_array(arena, C_Stripe, c_shared->key_stripes_count);
|
||||
c_shared->key_stripes_free_nodes = push_array(arena, C_KeyNode *, c_shared->key_stripes_count);
|
||||
for(U64 idx = 0; idx < c_shared->key_stripes_count; idx += 1)
|
||||
{
|
||||
C_Stripe *stripe = &c_shared->key_stripes[idx];
|
||||
stripe->arena = arena_alloc();
|
||||
stripe->rw_mutex = rw_mutex_alloc();
|
||||
stripe->cv = cond_var_alloc();
|
||||
}
|
||||
c_shared->root_slots_count = 4096;
|
||||
c_shared->root_stripes_count = Min(c_shared->root_slots_count, os_get_system_info()->logical_processor_count);
|
||||
c_shared->root_slots = push_array(arena, C_RootSlot, c_shared->root_slots_count);
|
||||
c_shared->root_stripes = push_array(arena, C_Stripe, c_shared->root_stripes_count);
|
||||
c_shared->root_stripes_free_nodes = push_array(arena, C_RootNode *, c_shared->root_stripes_count);
|
||||
for(U64 idx = 0; idx < c_shared->root_stripes_count; idx += 1)
|
||||
{
|
||||
C_Stripe *stripe = &c_shared->root_stripes[idx];
|
||||
stripe->arena = arena_alloc();
|
||||
stripe->rw_mutex = rw_mutex_alloc();
|
||||
stripe->cv = cond_var_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Root Allocation/Deallocation
|
||||
|
||||
internal C_Root
|
||||
c_root_alloc(void)
|
||||
{
|
||||
C_Root root = {0};
|
||||
root.u64[0] = ins_atomic_u64_inc_eval(&c_shared->root_id_gen);
|
||||
U64 slot_idx = root.u64[0]%c_shared->root_slots_count;
|
||||
U64 stripe_idx = slot_idx%c_shared->root_stripes_count;
|
||||
C_RootSlot *slot = &c_shared->root_slots[slot_idx];
|
||||
C_Stripe *stripe = &c_shared->root_stripes[stripe_idx];
|
||||
MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
C_RootNode *node = c_shared->root_stripes_free_nodes[stripe_idx];
|
||||
if(node != 0)
|
||||
{
|
||||
SLLStackPop(c_shared->root_stripes_free_nodes[stripe_idx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = push_array(stripe->arena, C_RootNode, 1);
|
||||
}
|
||||
DLLPushBack(slot->first, slot->last, node);
|
||||
node->root = root;
|
||||
node->arena = arena_alloc();
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
internal void
|
||||
c_root_release(C_Root root)
|
||||
{
|
||||
//- rjf: unpack root
|
||||
U64 slot_idx = root.u64[0]%c_shared->root_slots_count;
|
||||
U64 stripe_idx = slot_idx%c_shared->root_stripes_count;
|
||||
C_RootSlot *slot = &c_shared->root_slots[slot_idx];
|
||||
C_Stripe *stripe = &c_shared->root_stripes[stripe_idx];
|
||||
|
||||
//- rjf: release root node, grab its arena / ID list
|
||||
Arena *root_arena = 0;
|
||||
C_RootIDChunkList root_ids = {0};
|
||||
MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
for(C_RootNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(MemoryMatchStruct(&root, &n->root))
|
||||
{
|
||||
DLLRemove(slot->first, slot->last, n);
|
||||
root_arena = n->arena;
|
||||
root_ids = n->ids;
|
||||
SLLStackPush(c_shared->root_stripes_free_nodes[stripe_idx], n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: release all IDs
|
||||
for(C_RootIDChunkNode *id_chunk_n = root_ids.first; id_chunk_n != 0; id_chunk_n = id_chunk_n->next)
|
||||
{
|
||||
for EachIndex(chunk_idx, id_chunk_n->count)
|
||||
{
|
||||
C_ID id = id_chunk_n->v[chunk_idx];
|
||||
C_Key key = c_key_make(root, id);
|
||||
c_close_key(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Submission
|
||||
|
||||
internal U128
|
||||
c_submit_data(C_Key key, Arena **data_arena, String8 data)
|
||||
{
|
||||
//- rjf: unpack key
|
||||
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
|
||||
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
|
||||
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
|
||||
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
|
||||
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
|
||||
|
||||
//- rjf: hash data, unpack hash
|
||||
U128 hash = u128_hash_from_str8(data);
|
||||
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
|
||||
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
|
||||
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
|
||||
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
|
||||
|
||||
//- rjf: commit to (hash -> data) cache
|
||||
ProfScope("commit to (hash -> data) cache") RWMutexScope(stripe->rw_mutex, 1)
|
||||
{
|
||||
// rjf: find existing node
|
||||
C_BlobNode *node = 0;
|
||||
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(n->hash, hash))
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: release duplicate data if node already exists
|
||||
if(node != 0 && data_arena != 0 && *data_arena != 0)
|
||||
{
|
||||
arena_release(*data_arena);
|
||||
}
|
||||
|
||||
// rjf: allocate node if needed
|
||||
if(node == 0)
|
||||
{
|
||||
node = c_shared->blob_stripes_free_nodes[stripe_idx];
|
||||
if(node)
|
||||
{
|
||||
SLLStackPop(c_shared->blob_stripes_free_nodes[stripe_idx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = push_array_no_zero(stripe->arena, C_BlobNode, 1);
|
||||
}
|
||||
MemoryZeroStruct(node);
|
||||
node->hash = hash;
|
||||
if(data_arena != 0)
|
||||
{
|
||||
node->arena = *data_arena;
|
||||
}
|
||||
node->data = data;
|
||||
DLLPushBack(slot->first, slot->last, node);
|
||||
}
|
||||
|
||||
// rjf: bump key ref count
|
||||
node->key_ref_count += 1;
|
||||
|
||||
// rjf "steal" arena from caller
|
||||
if(data_arena != 0)
|
||||
{
|
||||
*data_arena = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: commit to (key -> list(hash)) cache
|
||||
U128 key_expired_hash = {0};
|
||||
ProfScope("commit to (key -> list(hash)) cache") RWMutexScope(key_stripe->rw_mutex, 1)
|
||||
{
|
||||
// rjf: find existing key
|
||||
C_KeyNode *key_node = 0;
|
||||
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(c_key_match(n->key, key))
|
||||
{
|
||||
key_node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: create key node if it doesn't exist
|
||||
B32 key_is_new = 0;
|
||||
if(!key_node)
|
||||
{
|
||||
key_is_new = 1;
|
||||
key_node = c_shared->key_stripes_free_nodes[key_stripe_idx];
|
||||
if(key_node)
|
||||
{
|
||||
SLLStackPop(c_shared->key_stripes_free_nodes[key_stripe_idx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
key_node = push_array_no_zero(key_stripe->arena, C_KeyNode, 1);
|
||||
}
|
||||
MemoryZeroStruct(key_node);
|
||||
key_node->key = key;
|
||||
DLLPushBack(key_slot->first, key_slot->last, key_node);
|
||||
}
|
||||
|
||||
// rjf: push hash into key's history
|
||||
if(key_node)
|
||||
{
|
||||
if(key_node->hash_history_gen >= C_KEY_HASH_HISTORY_STRONG_REF_COUNT)
|
||||
{
|
||||
key_expired_hash = key_node->hash_history[(key_node->hash_history_gen-C_KEY_HASH_HISTORY_STRONG_REF_COUNT)%ArrayCount(key_node->hash_history)];
|
||||
}
|
||||
key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)] = hash;
|
||||
key_node->hash_history_gen += 1;
|
||||
}
|
||||
|
||||
// rjf: key is new -> add this key to the associated root
|
||||
if(key_is_new)
|
||||
{
|
||||
U64 root_hash = u64_hash_from_str8(str8_struct(&key.root));
|
||||
U64 root_slot_idx = root_hash%c_shared->root_slots_count;
|
||||
U64 root_stripe_idx = root_slot_idx%c_shared->root_stripes_count;
|
||||
C_RootSlot *root_slot = &c_shared->root_slots[root_slot_idx];
|
||||
C_Stripe *root_stripe = &c_shared->root_stripes[root_stripe_idx];
|
||||
RWMutexScope(root_stripe->rw_mutex, 1)
|
||||
{
|
||||
for(C_RootNode *n = root_slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(MemoryMatchStruct(&n->root, &key.root))
|
||||
{
|
||||
C_RootIDChunkNode *chunk = n->ids.last;
|
||||
if(chunk == 0 || chunk->count >= chunk->cap)
|
||||
{
|
||||
chunk = push_array(n->arena, C_RootIDChunkNode, 1);
|
||||
SLLQueuePush(n->ids.first, n->ids.last, chunk);
|
||||
n->ids.chunk_count += 1;
|
||||
chunk->cap = 1024;
|
||||
chunk->v = push_array_no_zero(n->arena, C_ID, chunk->cap);
|
||||
}
|
||||
chunk->v[chunk->count] = key.id;
|
||||
chunk->count += 1;
|
||||
n->ids.total_count += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: decrement key ref count of expired hash
|
||||
if(!u128_match(key_expired_hash, u128_zero())) ProfScope("decrement key ref count of expired hash")
|
||||
{
|
||||
U64 old_hash_slot_idx = key_expired_hash.u64[1]%c_shared->blob_slots_count;
|
||||
U64 old_hash_stripe_idx = old_hash_slot_idx%c_shared->blob_stripes_count;
|
||||
C_BlobSlot *old_hash_slot = &c_shared->blob_slots[old_hash_slot_idx];
|
||||
C_Stripe *old_hash_stripe = &c_shared->blob_stripes[old_hash_stripe_idx];
|
||||
RWMutexScope(old_hash_stripe->rw_mutex, 0)
|
||||
{
|
||||
for(C_BlobNode *n = old_hash_slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(n->hash, key_expired_hash))
|
||||
{
|
||||
ins_atomic_u64_dec_eval(&n->key_ref_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Key Closing
|
||||
|
||||
internal void
|
||||
c_close_key(C_Key key)
|
||||
{
|
||||
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
|
||||
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
|
||||
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
|
||||
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
|
||||
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
|
||||
RWMutexScope(key_stripe->rw_mutex, 1)
|
||||
{
|
||||
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(c_key_match(n->key, key))
|
||||
{
|
||||
for(U64 history_idx = 0;
|
||||
history_idx < C_KEY_HASH_HISTORY_STRONG_REF_COUNT && history_idx < n->hash_history_gen;
|
||||
history_idx += 1)
|
||||
{
|
||||
U128 hash = n->hash_history[(n->hash_history_gen-1-history_idx) % ArrayCount(n->hash_history)];
|
||||
U64 hash_slot_idx = hash.u64[1]%c_shared->blob_slots_count;
|
||||
U64 hash_stripe_idx = hash_slot_idx%c_shared->blob_stripes_count;
|
||||
C_BlobSlot *hash_slot = &c_shared->blob_slots[hash_slot_idx];
|
||||
C_Stripe *hash_stripe = &c_shared->blob_stripes[hash_stripe_idx];
|
||||
MutexScopeR(hash_stripe->rw_mutex)
|
||||
{
|
||||
for(C_BlobNode *n = hash_slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(n->hash, hash))
|
||||
{
|
||||
ins_atomic_u64_dec_eval(&n->key_ref_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DLLRemove(key_slot->first, key_slot->last, n);
|
||||
SLLStackPush(c_shared->key_stripes_free_nodes[key_stripe_idx], n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Downstream Accesses
|
||||
|
||||
internal void
|
||||
c_hash_downstream_inc(U128 hash)
|
||||
{
|
||||
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
|
||||
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
|
||||
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
|
||||
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
|
||||
MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(hash, n->hash))
|
||||
{
|
||||
ins_atomic_u64_inc_eval(&n->downstream_ref_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
c_hash_downstream_dec(U128 hash)
|
||||
{
|
||||
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
|
||||
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
|
||||
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
|
||||
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
|
||||
MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(hash, n->hash))
|
||||
{
|
||||
ins_atomic_u64_dec_eval(&n->downstream_ref_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookup
|
||||
|
||||
internal U128
|
||||
c_hash_from_key(C_Key key, U64 rewind_count)
|
||||
{
|
||||
U128 result = {0};
|
||||
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
|
||||
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
|
||||
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
|
||||
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
|
||||
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
|
||||
RWMutexScope(key_stripe->rw_mutex, 0)
|
||||
{
|
||||
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(c_key_match(n->key, key) && n->hash_history_gen > 0 && n->hash_history_gen-1 >= rewind_count)
|
||||
{
|
||||
result = n->hash_history[(n->hash_history_gen-1-rewind_count)%ArrayCount(n->hash_history)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
c_data_from_hash(Access *access, U128 hash)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
String8 result = {0};
|
||||
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
|
||||
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
|
||||
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
|
||||
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
|
||||
MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(n->hash, hash))
|
||||
{
|
||||
result = n->data;
|
||||
access_touch(access, &n->access_pt, stripe->cv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfEnd();
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Tick
|
||||
|
||||
internal void
|
||||
c_async_tick(void)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
|
||||
//- rjf: garbage collect blobs
|
||||
{
|
||||
Rng1U64 range = lane_range(c_shared->blob_slots_count);
|
||||
for EachInRange(slot_idx, range)
|
||||
{
|
||||
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
|
||||
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
|
||||
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
|
||||
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
|
||||
{
|
||||
B32 slot_has_work = 0;
|
||||
RWMutexScope(stripe->rw_mutex, write_mode)
|
||||
{
|
||||
for(C_BlobNode *n = slot->first, *next = 0; n != 0; n = next)
|
||||
{
|
||||
next = n->next;
|
||||
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
|
||||
U64 downstream_ref_count = ins_atomic_u64_eval(&n->downstream_ref_count);
|
||||
if(access_pt_is_expired(&n->access_pt, .time = 5000000) && key_ref_count == 0 && downstream_ref_count == 0)
|
||||
{
|
||||
slot_has_work = 1;
|
||||
if(!write_mode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
DLLRemove(slot->first, slot->last, n);
|
||||
SLLStackPush(c_shared->blob_stripes_free_nodes[stripe_idx], n);
|
||||
if(n->arena != 0)
|
||||
{
|
||||
arena_release(n->arena);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!slot_has_work)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProfEnd();
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CONTENT_H
|
||||
#define CONTENT_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(rjf): Hash Store Notes (2025/05/18)
|
||||
//
|
||||
// The hash store is a general-purpose data cache. It offers three layers of
|
||||
// caching: (a) content (hash of data), (b) key (unique identity correllated
|
||||
// with history of hashes), and (c) root (bucket for many keys, manually
|
||||
// allocated / deallocated).
|
||||
//
|
||||
// (a) The "content" level of cache access is a simply hash(data) -> data
|
||||
// mapping. This bypasses all identity/key/root mechanisms and provides a
|
||||
// way to just talk about unique (and deduplicated) blobs of data.
|
||||
//
|
||||
// (b) The "key" level of cache access is used to encode a history of hashes
|
||||
// for some unique "identity", where the "identity" is a concept managed
|
||||
// by the user. One example of an identity would be a particular address
|
||||
// range inside of some process to which the debugger is attached. Another
|
||||
// might be a range inside of some file.
|
||||
//
|
||||
// (c) The "root" level is to provide a top-level allocation/deallocation
|
||||
// mechanism for a large set of keys. It also provides an extra level of
|
||||
// key uniqueness. For instance, each process to which the debugger is
|
||||
// attached might have its own root, and each key might correspond to a
|
||||
// particular address range within that process. This way, when the
|
||||
// process ends, all of its keys can be easily destroyed using a single
|
||||
// deallocation of the root.
|
||||
//
|
||||
// The way this might be generally used inside of the debugger would be that
|
||||
// some evaluation - let's say it's some variable `x` - is mapped (via debug
|
||||
// info) to some address range. If `x` is a `char[4096]`, then it might map
|
||||
// to some address range [&x, &x + 4096). This, together with the process
|
||||
// within which `x` is evaluated, forms both a `root` (for the process) and
|
||||
// a `key` (for the address range). Some asynchronous memory streaming system
|
||||
// can then, together with the root and key, read memory for that range, then
|
||||
// submit that data to the hash store, correllating with the root and key
|
||||
// combo.
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Key Types
|
||||
|
||||
typedef struct C_Root C_Root;
|
||||
struct C_Root
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct C_ID C_ID;
|
||||
struct C_ID
|
||||
{
|
||||
U128 u128[1];
|
||||
};
|
||||
|
||||
typedef struct C_Key C_Key;
|
||||
struct C_Key
|
||||
{
|
||||
C_Root root;
|
||||
U64 _padding_;
|
||||
C_ID id;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Stripe Type
|
||||
|
||||
typedef struct C_Stripe C_Stripe;
|
||||
struct C_Stripe
|
||||
{
|
||||
Arena *arena;
|
||||
RWMutex rw_mutex;
|
||||
CondVar cv;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Root Cache Types
|
||||
|
||||
typedef struct C_RootIDChunkNode C_RootIDChunkNode;
|
||||
struct C_RootIDChunkNode
|
||||
{
|
||||
C_RootIDChunkNode *next;
|
||||
C_ID *v;
|
||||
U64 count;
|
||||
U64 cap;
|
||||
};
|
||||
|
||||
typedef struct C_RootIDChunkList C_RootIDChunkList;
|
||||
struct C_RootIDChunkList
|
||||
{
|
||||
C_RootIDChunkNode *first;
|
||||
C_RootIDChunkNode *last;
|
||||
U64 chunk_count;
|
||||
U64 total_count;
|
||||
};
|
||||
|
||||
typedef struct C_RootNode C_RootNode;
|
||||
struct C_RootNode
|
||||
{
|
||||
C_RootNode *next;
|
||||
C_RootNode *prev;
|
||||
Arena *arena;
|
||||
C_Root root;
|
||||
C_RootIDChunkList ids;
|
||||
};
|
||||
|
||||
typedef struct C_RootSlot C_RootSlot;
|
||||
struct C_RootSlot
|
||||
{
|
||||
C_RootNode *first;
|
||||
C_RootNode *last;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Key Cache Types
|
||||
|
||||
#define C_KEY_HASH_HISTORY_COUNT 64
|
||||
#define C_KEY_HASH_HISTORY_STRONG_REF_COUNT 2
|
||||
|
||||
typedef struct C_KeyNode C_KeyNode;
|
||||
struct C_KeyNode
|
||||
{
|
||||
C_KeyNode *next;
|
||||
C_KeyNode *prev;
|
||||
C_Key key;
|
||||
U128 hash_history[C_KEY_HASH_HISTORY_COUNT];
|
||||
U64 hash_history_gen;
|
||||
};
|
||||
|
||||
typedef struct C_KeySlot C_KeySlot;
|
||||
struct C_KeySlot
|
||||
{
|
||||
C_KeyNode *first;
|
||||
C_KeyNode *last;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Content Blob Cache Types
|
||||
|
||||
typedef struct C_BlobNode C_BlobNode;
|
||||
struct C_BlobNode
|
||||
{
|
||||
C_BlobNode *next;
|
||||
C_BlobNode *prev;
|
||||
U128 hash;
|
||||
Arena *arena;
|
||||
String8 data;
|
||||
AccessPt access_pt;
|
||||
U64 key_ref_count;
|
||||
U64 downstream_ref_count;
|
||||
};
|
||||
|
||||
typedef struct C_BlobSlot C_BlobSlot;
|
||||
struct C_BlobSlot
|
||||
{
|
||||
C_BlobNode *first;
|
||||
C_BlobNode *last;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Shared State
|
||||
|
||||
typedef struct C_Shared C_Shared;
|
||||
struct C_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
|
||||
// rjf: main data blob cache
|
||||
U64 blob_slots_count;
|
||||
U64 blob_stripes_count;
|
||||
C_BlobSlot *blob_slots;
|
||||
C_Stripe *blob_stripes;
|
||||
C_BlobNode **blob_stripes_free_nodes;
|
||||
|
||||
// rjf: key cache
|
||||
U64 key_slots_count;
|
||||
U64 key_stripes_count;
|
||||
C_KeySlot *key_slots;
|
||||
C_Stripe *key_stripes;
|
||||
C_KeyNode **key_stripes_free_nodes;
|
||||
|
||||
// rjf: root cache
|
||||
U64 root_slots_count;
|
||||
U64 root_stripes_count;
|
||||
C_RootSlot *root_slots;
|
||||
C_Stripe *root_stripes;
|
||||
C_RootNode **root_stripes_free_nodes;
|
||||
U64 root_id_gen;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global C_Shared *c_shared = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal C_ID c_id_make(U64 u64_0, U64 u64_1);
|
||||
internal B32 c_id_match(C_ID a, C_ID b);
|
||||
internal C_Key c_key_make(C_Root root, C_ID id);
|
||||
internal B32 c_key_match(C_Key a, C_Key b);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void c_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Root Allocation/Deallocation
|
||||
|
||||
internal C_Root c_root_alloc(void);
|
||||
internal void c_root_release(C_Root root);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Submission
|
||||
|
||||
internal U128 c_submit_data(C_Key key, Arena **data_arena, String8 data);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Key Closing
|
||||
|
||||
internal void c_close_key(C_Key key);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Downstream Accesses
|
||||
|
||||
internal void c_hash_downstream_inc(U128 hash);
|
||||
internal void c_hash_downstream_dec(U128 hash);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal U128 c_hash_from_key(C_Key key, U64 rewind_count);
|
||||
internal String8 c_data_from_hash(Access *access, U128 hash);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Tick
|
||||
|
||||
internal void c_async_tick(void);
|
||||
|
||||
#endif // CONTENT_H
|
||||
+81
-192
@@ -1,192 +1,81 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Entity Kinds
|
||||
|
||||
@table(name code_name display_string)
|
||||
CTRL_EntityKindTable:
|
||||
{
|
||||
{Root root "Root" }
|
||||
{Machine machine "Machine" }
|
||||
{Process process "Process" }
|
||||
{Thread thread "Thread" }
|
||||
{Module module "Module" }
|
||||
{EntryPoint entry_point "Entry Point" }
|
||||
{DebugInfoPath debug_info_path "Debug Info Path" }
|
||||
{PendingThreadName pending_thread_name "Pending Thread Name" }
|
||||
{PendingThreadColor pending_thread_color "Pending Thread Color" }
|
||||
{Breakpoint breakpoint "Breakpoint" }
|
||||
{AddressRangeAnnotation address_range_annotation "Address Range Annotation" }
|
||||
}
|
||||
|
||||
@enum CTRL_EntityKind:
|
||||
{
|
||||
Null,
|
||||
@expand(CTRL_EntityKindTable a) `$(a.name)`,
|
||||
COUNT,
|
||||
}
|
||||
|
||||
@data(String8) ctrl_entity_kind_code_name_table:
|
||||
{
|
||||
`{0}`,
|
||||
@expand(CTRL_EntityKindTable a) `str8_lit_comp("$(a.code_name)")`
|
||||
}
|
||||
|
||||
@data(String8) ctrl_entity_kind_display_string_table:
|
||||
{
|
||||
`{0}`,
|
||||
@expand(CTRL_EntityKindTable a) `str8_lit_comp("$(a.display_name)")`
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Exception Codes
|
||||
|
||||
@table(name lower_name code default display_string)
|
||||
CTRL_ExceptionCodeKindTable:
|
||||
{
|
||||
{Win32CtrlC win32_ctrl_c 0x40010005 1 "(Win32) Control-C" }
|
||||
{Win32CtrlBreak win32_ctrl_break 0x40010008 1 "(Win32) Control-Break" }
|
||||
{Win32WinRTOriginateError win32_win_rt_originate_error 0x40080201 0 "(Win32) WinRT Originate Error" }
|
||||
{Win32WinRTTransformError win32_win_rt_transform_error 0x40080202 0 "(Win32) WinRT Transform Error" }
|
||||
{Win32RPCCallCancelled win32_rpc_call_cancelled 0x0000071a 0 "(Win32) RPC Call Cancelled" }
|
||||
{Win32DatatypeMisalignment win32_datatype_misalignment 0x80000002 0 "(Win32) Data Type Misalignment" }
|
||||
{Win32AccessViolation win32_access_violation 0xc0000005 1 "(Win32) Access Violation" }
|
||||
{Win32InPageError win32_in_page_error 0xc0000006 0 "(Win32) In Page Error" }
|
||||
{Win32InvalidHandle win32_invalid_handle 0xc0000008 1 "(Win32) Invalid Handle Specified" }
|
||||
{Win32NotEnoughQuota win32_not_enough_quota 0xc0000017 0 "(Win32) Not Enough Quota" }
|
||||
{Win32IllegalInstruction win32_illegal_instruction 0xc000001d 0 "(Win32) Illegal Instruction" }
|
||||
{Win32CannotContinueException win32_cannot_continue_exception 0xc0000025 0 "(Win32) Cannot Continue From Exception" }
|
||||
{Win32InvalidExceptionDisposition win32_invalid_exception_disposition 0xc0000026 0 "(Win32) Invalid Exception Disposition Returned By Handler" }
|
||||
{Win32ArrayBoundsExceeded win32_array_bounds_exceeded 0xc000008c 0 "(Win32) Array Bounds Exceeded" }
|
||||
{Win32FloatingPointDenormalOperand win32_floating_point_denormal_operand 0xc000008d 0 "(Win32) Floating-Point Denormal Operand" }
|
||||
{Win32FloatingPointDivisionByZero win32_floating_point_division_by_zero 0xc000008e 0 "(Win32) Floating-Point Division By Zero" }
|
||||
{Win32FloatingPointInexactResult win32_floating_point_inexact_result 0xc000008f 0 "(Win32) Floating-Point Inexact Result" }
|
||||
{Win32FloatingPointInvalidOperation win32_floating_point_invalid_operation 0xc0000090 0 "(Win32) Floating-Point Invalid Operation" }
|
||||
{Win32FloatingPointOverflow win32_floating_point_overflow 0xc0000091 0 "(Win32) Floating-Point Overflow" }
|
||||
{Win32FloatingPointStackCheck win32_floating_point_stack_check 0xc0000092 0 "(Win32) Floating-Point Stack Check" }
|
||||
{Win32FloatingPointUnderflow win32_floating_point_underflow 0xc0000093 0 "(Win32) Floating-Point Underflow" }
|
||||
{Win32IntegerDivisionByZero win32_integer_division_by_zero 0xc0000094 0 "(Win32) Integer Division By Zero" }
|
||||
{Win32IntegerOverflow win32_integer_overflow 0xc0000095 0 "(Win32) Integer Overflow" }
|
||||
{Win32PrivilegedInstruction win32_privileged_instruction 0xc0000096 0 "(Win32) Privileged Instruction" }
|
||||
{Win32StackOverflow win32_stack_overflow 0xc00000fd 0 "(Win32) Stack Overflow" }
|
||||
{Win32UnableToLocateDLL win32_unable_to_locate_dll 0xc0000135 0 "(Win32) Unable To Locate DLL" }
|
||||
{Win32OrdinalNotFound win32_ordinal_not_found 0xc0000138 0 "(Win32) Ordinal Not Found" }
|
||||
{Win32EntryPointNotFound win32_entry_point_not_found 0xc0000139 0 "(Win32) Entry Point Not Found" }
|
||||
{Win32DLLInitializationFailed win32_dll_initialization_failed 0xc0000142 0 "(Win32) DLL Initialization Failed" }
|
||||
{Win32FloatingPointSSEMultipleFaults win32_floating_point_sse_multiple_faults 0xc00002b4 0 "(Win32) Floating Point SSE Multiple Faults" }
|
||||
{Win32FloatingPointSSEMultipleTraps win32_floating_point_sse_multiple_traps 0xc00002b5 0 "(Win32) Floating Point SSE Multiple Traps" }
|
||||
{Win32AssertionFailed win32_assertion_failed 0xc0000420 1 "(Win32) Assertion Failed" }
|
||||
{Win32ModuleNotFound win32_module_not_found 0xc06d007e 0 "(Win32) Module Not Found" }
|
||||
{Win32ProcedureNotFound win32_procedure_not_found 0xc06d007f 0 "(Win32) Procedure Not Found" }
|
||||
{Win32SanitizerErrorDetected win32_sanitizer_error_detected 0xe073616e 1 "(Win32) Sanitizer Error Detected" }
|
||||
{Win32SanitizerRawAccessViolation win32_sanitizer_raw_access_violation 0xe0736171 0 "(Win32) Sanitizer Raw Access Violation" }
|
||||
{Win32DirectXDebugLayer win32_directx_debug_layer 0x0000087a 1 "(Win32) DirectX Debug Layer" }
|
||||
}
|
||||
|
||||
@enum CTRL_ExceptionCodeKind:
|
||||
{
|
||||
Null,
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `$(a.name)`,
|
||||
COUNT,
|
||||
}
|
||||
|
||||
@data(U32) ctrl_exception_code_kind_code_table:
|
||||
{
|
||||
`0`;
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `$(a.code)`;
|
||||
}
|
||||
|
||||
@data(String8) ctrl_exception_code_kind_display_string_table:
|
||||
{
|
||||
`{0}`;
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `str8_lit_comp("$(a.display_string)")`;
|
||||
}
|
||||
|
||||
@data(String8) ctrl_exception_code_kind_lowercase_code_string_table:
|
||||
{
|
||||
`{0}`;
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `str8_lit_comp("$(a.lower_name)")`;
|
||||
}
|
||||
|
||||
@data(B8) ctrl_exception_code_kind_default_enable_table:
|
||||
{
|
||||
`0`;
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `$(a.default)`;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Exception Sub-Codes
|
||||
|
||||
@table(name, value)
|
||||
CTRL_ExceptionSubCodeKindTable:
|
||||
{
|
||||
{ W32_FAST_FAIL_LEGACY_GS_VIOLATION 0 }
|
||||
{ W32_FAST_FAIL_VTGUARD_CHECK_FAILURE 1 }
|
||||
{ W32_FAST_FAIL_STACK_COOKIE_CHECK_FAILURE 2 }
|
||||
{ W32_FAST_FAIL_CORRUPT_LIST_ENTRY 3 }
|
||||
{ W32_FAST_FAIL_INCORRECT_STACK 4 }
|
||||
{ W32_FAST_FAIL_INVALID_ARG 5 }
|
||||
{ W32_FAST_FAIL_GS_COOKIE_INIT 6 }
|
||||
{ W32_FAST_FAIL_FATAL_APP_EXIT 7 }
|
||||
{ W32_FAST_FAIL_RANGE_CHECK_FAILURE 8 }
|
||||
{ W32_FAST_FAIL_UNSAFE_REGISTRY_ACCESS 9 }
|
||||
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_FAILURE 10 }
|
||||
{ W32_FAST_FAIL_GUARD_WRITE_CHECK_FAILURE 11 }
|
||||
{ W32_FAST_FAIL_INVALID_FIBER_SWITCH 12 }
|
||||
{ W32_FAST_FAIL_INVALID_SET_OF_CONTEXT 13 }
|
||||
{ W32_FAST_FAIL_INVALID_REFERENCE_COUNT 14 }
|
||||
{ W32_FAST_FAIL_INVALID_JUMP_BUFFER 18 }
|
||||
{ W32_FAST_FAIL_MRDATA_MODIFIED 19 }
|
||||
{ W32_FAST_FAIL_CERTIFICATION_FAILURE 20 }
|
||||
{ W32_FAST_FAIL_INVALID_EXCEPTION_CHAIN 21 }
|
||||
{ W32_FAST_FAIL_CRYPTO_LIBRARY 22 }
|
||||
{ W32_FAST_FAIL_INVALID_CALL_IN_DLL_CALLOUT 23 }
|
||||
{ W32_FAST_FAIL_INVALID_IMAGE_BASE 24 }
|
||||
{ W32_FAST_FAIL_DLOAD_PROTECTION_FAILURE 25 }
|
||||
{ W32_FAST_FAIL_UNSAFE_EXTENSION_CALL 26 }
|
||||
{ W32_FAST_FAIL_DEPRECATED_SERVICE_INVOKED 27 }
|
||||
{ W32_FAST_FAIL_INVALID_BUFFER_ACCESS 28 }
|
||||
{ W32_FAST_FAIL_INVALID_BALANCED_TREE 29 }
|
||||
{ W32_FAST_FAIL_INVALID_NEXT_THREAD 30 }
|
||||
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_SUPPRESSED 31 }
|
||||
{ W32_FAST_FAIL_APCS_DISABLED 32 }
|
||||
{ W32_FAST_FAIL_INVALID_IDLE_STATE 33 }
|
||||
{ W32_FAST_FAIL_MRDATA_PROTECTION_FAILURE 34 }
|
||||
{ W32_FAST_FAIL_UNEXPECTED_HEAP_EXCEPTION 35 }
|
||||
{ W32_FAST_FAIL_INVALID_LOCK_STATE 36 }
|
||||
{ W32_FAST_FAIL_GUARD_JUMPTABLE 37 }
|
||||
{ W32_FAST_FAIL_INVALID_LONGJUMP_TARGET 38 }
|
||||
{ W32_FAST_FAIL_INVALID_DISPATCH_CONTEXT 39 }
|
||||
{ W32_FAST_FAIL_INVALID_THREAD 40 }
|
||||
{ W32_FAST_FAIL_INVALID_SYSCALL_NUMBER 41 }
|
||||
{ W32_FAST_FAIL_INVALID_FILE_OPERATION 42 }
|
||||
{ W32_FAST_FAIL_LPAC_ACCESS_DENIED 43 }
|
||||
{ W32_FAST_FAIL_GUARD_SS_FAILURE 44 }
|
||||
{ W32_FAST_FAIL_LOADER_CONTINUITY_FAILURE 45 }
|
||||
{ W32_FAST_FAIL_GUARD_EXPORT_SUPPRESSION_FAILURE 46 }
|
||||
{ W32_FAST_FAIL_INVALID_CONTROL_STACK 47 }
|
||||
{ W32_FAST_FAIL_SET_CONTEXT_DENIED 48 }
|
||||
{ W32_FAST_FAIL_INVALID_IAT 49 }
|
||||
{ W32_FAST_FAIL_HEAP_METADATA_CORRUPTION 50 }
|
||||
{ W32_FAST_FAIL_PAYLOAD_RESTRICTION_VIOLATION 51 }
|
||||
{ W32_FAST_FAIL_LOW_LABEL_ACCESS_DENIED 52 }
|
||||
{ W32_FAST_FAIL_ENCLAVE_CALL_FAILURE 53 }
|
||||
{ W32_FAST_FAIL_UNHANDLED_LSS_EXCEPTON 54 }
|
||||
{ W32_FAST_FAIL_ADMINLESS_ACCESS_DENIED 55 }
|
||||
{ W32_FAST_FAIL_UNEXPECTED_CALL 56 }
|
||||
{ W32_FAST_FAIL_CONTROL_INVALID_RETURN_ADDRESS 57 }
|
||||
{ W32_FAST_FAIL_UNEXPECTED_HOST_BEHAVIOR 58 }
|
||||
{ W32_FAST_FAIL_FLAGS_CORRUPTION 59 }
|
||||
{ W32_FAST_FAIL_VEH_CORRUPTION 60 }
|
||||
{ W32_FAST_FAIL_ETW_CORRUPTION 61 }
|
||||
{ W32_FAST_FAIL_RIO_ABORT 62 }
|
||||
{ W32_FAST_FAIL_INVALID_PFN 63 }
|
||||
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_FAILURE_XFG 64 }
|
||||
{ W32_FAST_FAIL_CAST_GUARD 65 }
|
||||
{ W32_FAST_FAIL_HOST_VISIBILITY_CHANGE 66 }
|
||||
{ W32_FAST_FAIL_KERNEL_CET_SHADOW_STACK_ASSIST 67 }
|
||||
{ W32_FAST_FAIL_PATCH_CALLBACK_FAILED 68 }
|
||||
{ W32_FAST_FAIL_NTDLL_PATCH_FAILED 69 }
|
||||
{ W32_FAST_FAIL_INVALID_FLS_DATA 70 }
|
||||
}
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Tables
|
||||
|
||||
@table(name lower_name code default display_string)
|
||||
CTRL_ExceptionCodeKindTable:
|
||||
{
|
||||
{Win32CtrlC win32_ctrl_c 0x40010005 1 "(Win32) Control-C" }
|
||||
{Win32CtrlBreak win32_ctrl_break 0x40010008 1 "(Win32) Control-Break" }
|
||||
{Win32WinRTOriginateError win32_win_rt_originate_error 0x40080201 0 "(Win32) WinRT Originate Error" }
|
||||
{Win32WinRTTransformError win32_win_rt_transform_error 0x40080202 0 "(Win32) WinRT Transform Error" }
|
||||
{Win32RPCCallCancelled win32_rpc_call_cancelled 0x0000071a 0 "(Win32) RPC Call Cancelled" }
|
||||
{Win32DatatypeMisalignment win32_datatype_misalignment 0x80000002 0 "(Win32) Data Type Misalignment" }
|
||||
{Win32AccessViolation win32_access_violation 0xc0000005 1 "(Win32) Access Violation" }
|
||||
{Win32InPageError win32_in_page_error 0xc0000006 0 "(Win32) In Page Error" }
|
||||
{Win32InvalidHandle win32_invalid_handle 0xc0000008 1 "(Win32) Invalid Handle Specified" }
|
||||
{Win32NotEnoughQuota win32_not_enough_quota 0xc0000017 0 "(Win32) Not Enough Quota" }
|
||||
{Win32IllegalInstruction win32_illegal_instruction 0xc000001d 0 "(Win32) Illegal Instruction" }
|
||||
{Win32CannotContinueException win32_cannot_continue_exception 0xc0000025 0 "(Win32) Cannot Continue From Exception" }
|
||||
{Win32InvalidExceptionDisposition win32_invalid_exception_disposition 0xc0000026 0 "(Win32) Invalid Exception Disposition Returned By Handler" }
|
||||
{Win32ArrayBoundsExceeded win32_array_bounds_exceeded 0xc000008c 0 "(Win32) Array Bounds Exceeded" }
|
||||
{Win32FloatingPointDenormalOperand win32_floating_point_denormal_operand 0xc000008d 0 "(Win32) Floating-Point Denormal Operand" }
|
||||
{Win32FloatingPointDivisionByZero win32_floating_point_division_by_zero 0xc000008e 0 "(Win32) Floating-Point Division By Zero" }
|
||||
{Win32FloatingPointInexactResult win32_floating_point_inexact_result 0xc000008f 0 "(Win32) Floating-Point Inexact Result" }
|
||||
{Win32FloatingPointInvalidOperation win32_floating_point_invalid_operation 0xc0000090 0 "(Win32) Floating-Point Invalid Operation" }
|
||||
{Win32FloatingPointOverflow win32_floating_point_overflow 0xc0000091 0 "(Win32) Floating-Point Overflow" }
|
||||
{Win32FloatingPointStackCheck win32_floating_point_stack_check 0xc0000092 0 "(Win32) Floating-Point Stack Check" }
|
||||
{Win32FloatingPointUnderflow win32_floating_point_underflow 0xc0000093 0 "(Win32) Floating-Point Underflow" }
|
||||
{Win32IntegerDivisionByZero win32_integer_division_by_zero 0xc0000094 0 "(Win32) Integer Division By Zero" }
|
||||
{Win32IntegerOverflow win32_integer_overflow 0xc0000095 0 "(Win32) Integer Overflow" }
|
||||
{Win32PrivilegedInstruction win32_privileged_instruction 0xc0000096 0 "(Win32) Privileged Instruction" }
|
||||
{Win32StackOverflow win32_stack_overflow 0xc00000fd 0 "(Win32) Stack Overflow" }
|
||||
{Win32UnableToLocateDLL win32_unable_to_locate_dll 0xc0000135 0 "(Win32) Unable To Locate DLL" }
|
||||
{Win32OrdinalNotFound win32_ordinal_not_found 0xc0000138 0 "(Win32) Ordinal Not Found" }
|
||||
{Win32EntryPointNotFound win32_entry_point_not_found 0xc0000139 0 "(Win32) Entry Point Not Found" }
|
||||
{Win32DLLInitializationFailed win32_dll_initialization_failed 0xc0000142 0 "(Win32) DLL Initialization Failed" }
|
||||
{Win32FloatingPointSSEMultipleFaults win32_floating_point_sse_multiple_faults 0xc00002b4 0 "(Win32) Floating Point SSE Multiple Faults" }
|
||||
{Win32FloatingPointSSEMultipleTraps win32_floating_point_sse_multiple_traps 0xc00002b5 0 "(Win32) Floating Point SSE Multiple Traps" }
|
||||
{Win32AssertionFailed win32_assertion_failed 0xc0000420 1 "(Win32) Assertion Failed" }
|
||||
{Win32ModuleNotFound win32_module_not_found 0xc06d007e 0 "(Win32) Module Not Found" }
|
||||
{Win32ProcedureNotFound win32_procedure_not_found 0xc06d007f 0 "(Win32) Procedure Not Found" }
|
||||
{Win32SanitizerErrorDetected win32_sanitizer_error_detected 0xe073616e 1 "(Win32) Sanitizer Error Detected" }
|
||||
{Win32SanitizerRawAccessViolation win32_sanitizer_raw_access_violation 0xe0736171 0 "(Win32) Sanitizer Raw Access Violation" }
|
||||
{Win32DirectXDebugLayer win32_directx_debug_layer 0x0000087a 1 "(Win32) DirectX Debug Layer" }
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Generators
|
||||
|
||||
@enum CTRL_ExceptionCodeKind:
|
||||
{
|
||||
Null,
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `$(a.name)`,
|
||||
COUNT,
|
||||
}
|
||||
|
||||
@data(U32) ctrl_exception_code_kind_code_table:
|
||||
{
|
||||
`0`;
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `$(a.code)`;
|
||||
}
|
||||
|
||||
@data(String8) ctrl_exception_code_kind_display_string_table:
|
||||
{
|
||||
`{0}`;
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `str8_lit_comp("$(a.display_string)")`;
|
||||
}
|
||||
|
||||
@data(String8) ctrl_exception_code_kind_lowercase_code_string_table:
|
||||
{
|
||||
`{0}`;
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `str8_lit_comp("$(a.lower_name)")`;
|
||||
}
|
||||
|
||||
@data(B8) ctrl_exception_code_kind_default_enable_table:
|
||||
{
|
||||
`0`;
|
||||
@expand(CTRL_ExceptionCodeKindTable a) `$(a.default)`;
|
||||
}
|
||||
|
||||
+5141
-6739
File diff suppressed because it is too large
Load Diff
+839
-1031
File diff suppressed because it is too large
Load Diff
+4
-7
@@ -1,7 +1,4 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#undef LAYER_COLOR
|
||||
#define LAYER_COLOR 0x969696ff
|
||||
|
||||
#include "ctrl_core.c"
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#include "ctrl_core.c"
|
||||
|
||||
+77
-77
@@ -1,77 +1,77 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CTRL_INC_H
|
||||
#define CTRL_INC_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(rjf): Control Layer Overview (2023/8/29)
|
||||
//
|
||||
// This layer's purpose is to provide access to the asynchronously-running, low
|
||||
// level parts of a debugger, running on the debugger client. This primarily
|
||||
// consists of process control, using the Demon layer (the lower level
|
||||
// abstraction layer for process control, across multiple OSes), but including
|
||||
// higher-level concepts, like stepping, breakpoint resolution, conditional
|
||||
// breakpoint evaluation, and so on. Right now, this just includes process
|
||||
// control *local to the debugger client machine*. But in the future, this can
|
||||
// also include communication to multiple target machines, all running their
|
||||
// own process controller, using the Demon layer.
|
||||
//
|
||||
// This part of a debugger must run asynchronously to prevent blocking the UI -
|
||||
// ideally our debugger is designed such that, if targets are running, the
|
||||
// debugger frontend is still usable for a variety of purposes. So, in short,
|
||||
// the asynchronously-running "control thread", implemented by this layer, is
|
||||
// tasked with communicating with a separately executing "user thread". This
|
||||
// communication happens in two directions - `user -> ctrl`, and the reverse,
|
||||
// `ctrl -> user`.
|
||||
//
|
||||
// In the case of `user -> ctrl` communication, this is done with a ring buffer
|
||||
// of "messages" (`CTRL_Msg`), pushed via `ctrl_u2c_push_msgs`. These messages
|
||||
// include commands like: launching targets, attaching to targets, killing
|
||||
// targets, detaching from targets, stepping/running, or single stepping.
|
||||
//
|
||||
// In the case of `ctrl -> user` communication, this is done with a ring buffer
|
||||
// of "events" (`CTRL_Event`), popped via `ctrl_c2u_pop_events`. These events
|
||||
// include information about what happened during the execution of targets -
|
||||
// including: process/module/thread creation, process/module/thread deletion,
|
||||
// debug strings, thread name events, memory allocation events, and stop events
|
||||
// (where stops can be caused by: user breakpoints, traps set for stepping,
|
||||
// exceptions, halts, or errors).
|
||||
//
|
||||
// The various stepping algorithms are implemented with two concepts: (a) the
|
||||
// "trap net", and (b) "spoofs".
|
||||
//
|
||||
// A "trap net" is a term which refers to a set of addresses paired with a set
|
||||
// of behavioral flags. Before targets run, trap instructions are written to
|
||||
// these addresses. After targets stop, these addresses are reset to their
|
||||
// original bytes. These trap instructions cause the debugger's targets to
|
||||
// stop executing, and based on which behavioral flags are associated with
|
||||
// the instruction causing the stop, the control thread may adjust parameters
|
||||
// used for running, then continue execution, or it will not resume target
|
||||
// execution, and will report stopped events. These behavioral flags can
|
||||
// include: single-stepping the stopped thread to execute the instruction at
|
||||
// the trap location, saving a stack pointer "check value" (where this check
|
||||
// value is compared against when making decisions about whether to continue
|
||||
// running or not), and so on. It's complicated to unpack why exactly these
|
||||
// behaviors are useful, but the TL;DR of it is that they are used for a
|
||||
// variety of stepping behaviors. For example, when doing a "step into" step,
|
||||
// a `call` instruction can have a trap set at it, and will be marked with
|
||||
// a "single-step-after" trap flag, as well as the "end stepping" trap flag,
|
||||
// such that the step operation will complete after the `call` has executed.
|
||||
//
|
||||
// A "spoof" is a feature the control layer uses to detect when some thread
|
||||
// returns from a particular sub-callstack. This is useful when implementing
|
||||
// "step over" in functions that may be recursive. In short, unlike a trap,
|
||||
// which writes a trap instruction (like `int3`) into an instruction stream,
|
||||
// a spoof overwrites a *return address* on some thread's *stack*. This return
|
||||
// address is not a valid address for executing code -- it is simply a value
|
||||
// that the debugger can recognize, such that it is notified when the thread
|
||||
// returns from some level in a callstack. When the thread exits some function,
|
||||
// it will return to the "spoofed" address, and it will immediately hit an
|
||||
// exception, because the spoofed address will not be a valid address for
|
||||
// code execution. At that point, the debugger can move the thread back to
|
||||
// the pre-spoof return address, and resume execution.
|
||||
|
||||
#include "ctrl_core.h"
|
||||
|
||||
#endif // CTRL_INC_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef CTRL_INC_H
|
||||
#define CTRL_INC_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(rjf): Control Layer Overview (2023/8/29)
|
||||
//
|
||||
// This layer's purpose is to provide access to the asynchronously-running, low
|
||||
// level parts of a debugger, running on the debugger client. This primarily
|
||||
// consists of process control, using the Demon layer (the lower level
|
||||
// abstraction layer for process control, across multiple OSes), but including
|
||||
// higher-level concepts, like stepping, breakpoint resolution, conditional
|
||||
// breakpoint evaluation, and so on. Right now, this just includes process
|
||||
// control *local to the debugger client machine*. But in the future, this can
|
||||
// also include communication to multiple target machines, all running their
|
||||
// own process controller, using the Demon layer.
|
||||
//
|
||||
// This part of a debugger must run asynchronously to prevent blocking the UI -
|
||||
// ideally our debugger is designed such that, if targets are running, the
|
||||
// debugger frontend is still usable for a variety of purposes. So, in short,
|
||||
// the asynchronously-running "control thread", implemented by this layer, is
|
||||
// tasked with communicating with a separately executing "user thread". This
|
||||
// communication happens in two directions - `user -> ctrl`, and the reverse,
|
||||
// `ctrl -> user`.
|
||||
//
|
||||
// In the case of `user -> ctrl` communication, this is done with a ring buffer
|
||||
// of "messages" (`CTRL_Msg`), pushed via `ctrl_u2c_push_msgs`. These messages
|
||||
// include commands like: launching targets, attaching to targets, killing
|
||||
// targets, detaching from targets, stepping/running, or single stepping.
|
||||
//
|
||||
// In the case of `ctrl -> user` communication, this is done with a ring buffer
|
||||
// of "events" (`CTRL_Event`), popped via `ctrl_c2u_pop_events`. These events
|
||||
// include information about what happened during the execution of targets -
|
||||
// including: process/module/thread creation, process/module/thread deletion,
|
||||
// debug strings, thread name events, memory allocation events, and stop events
|
||||
// (where stops can be caused by: user breakpoints, traps set for stepping,
|
||||
// exceptions, halts, or errors).
|
||||
//
|
||||
// The various stepping algorithms are implemented with two concepts: (a) the
|
||||
// "trap net", and (b) "spoofs".
|
||||
//
|
||||
// A "trap net" is a term which refers to a set of addresses paired with a set
|
||||
// of behavioral flags. Before targets run, trap instructions are written to
|
||||
// these addresses. After targets stop, these addresses are reset to their
|
||||
// original bytes. These trap instructions cause the debugger's targets to
|
||||
// stop executing, and based on which behavioral flags are associated with
|
||||
// the instruction causing the stop, the control thread may adjust parameters
|
||||
// used for running, then continue execution, or it will not resume target
|
||||
// execution, and will report stopped events. These behavioral flags can
|
||||
// include: single-stepping the stopped thread to execute the instruction at
|
||||
// the trap location, saving a stack pointer "check value" (where this check
|
||||
// value is compared against when making decisions about whether to continue
|
||||
// running or not), and so on. It's complicated to unpack why exactly these
|
||||
// behaviors are useful, but the TL;DR of it is that they are used for a
|
||||
// variety of stepping behaviors. For example, when doing a "step into" step,
|
||||
// a `call` instruction can have a trap set at it, and will be marked with
|
||||
// a "single-step-after" trap flag, as well as the "end stepping" trap flag,
|
||||
// such that the step operation will complete after the `call` has executed.
|
||||
//
|
||||
// A "spoof" is a feature the control layer uses to detect when some thread
|
||||
// returns from a particular sub-callstack. This is useful when implementing
|
||||
// "step over" in functions that may be recursive. In short, unlike a trap,
|
||||
// which writes a trap instruction (like `int3`) into an instruction stream,
|
||||
// a spoof overwrites a *return address* on some thread's *stack*. This return
|
||||
// address is not a valid address for executing code -- it is simply a value
|
||||
// that the debugger can recognize, such that it is notified when the thread
|
||||
// returns from some level in a callstack. When the thread exits some function,
|
||||
// it will return to the "spoofed" address, and it will immediately hit an
|
||||
// exception, because the spoofed address will not be a valid address for
|
||||
// code execution. At that point, the debugger can move the thread back to
|
||||
// the pre-spoof return address, and resume execution.
|
||||
|
||||
#include "ctrl_core.h"
|
||||
|
||||
#endif // CTRL_INC_H
|
||||
|
||||
+176
-208
@@ -1,208 +1,176 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
C_LINKAGE_BEGIN
|
||||
String8 ctrl_entity_kind_code_name_table[12] =
|
||||
{
|
||||
{0},
|
||||
str8_lit_comp("root"),
|
||||
str8_lit_comp("machine"),
|
||||
str8_lit_comp("process"),
|
||||
str8_lit_comp("thread"),
|
||||
str8_lit_comp("module"),
|
||||
str8_lit_comp("entry_point"),
|
||||
str8_lit_comp("debug_info_path"),
|
||||
str8_lit_comp("pending_thread_name"),
|
||||
str8_lit_comp("pending_thread_color"),
|
||||
str8_lit_comp("breakpoint"),
|
||||
str8_lit_comp("address_range_annotation"),
|
||||
};
|
||||
|
||||
String8 ctrl_entity_kind_display_string_table[12] =
|
||||
{
|
||||
{0},
|
||||
str8_lit_comp("Root"),
|
||||
str8_lit_comp("Machine"),
|
||||
str8_lit_comp("Process"),
|
||||
str8_lit_comp("Thread"),
|
||||
str8_lit_comp("Module"),
|
||||
str8_lit_comp("EntryPoint"),
|
||||
str8_lit_comp("DebugInfoPath"),
|
||||
str8_lit_comp("PendingThreadName"),
|
||||
str8_lit_comp("PendingThreadColor"),
|
||||
str8_lit_comp("Breakpoint"),
|
||||
str8_lit_comp("AddressRangeAnnotation"),
|
||||
};
|
||||
|
||||
U32 ctrl_exception_code_kind_code_table[38] =
|
||||
{
|
||||
0,
|
||||
0x40010005,
|
||||
0x40010008,
|
||||
0x40080201,
|
||||
0x40080202,
|
||||
0x0000071a,
|
||||
0x80000002,
|
||||
0xc0000005,
|
||||
0xc0000006,
|
||||
0xc0000008,
|
||||
0xc0000017,
|
||||
0xc000001d,
|
||||
0xc0000025,
|
||||
0xc0000026,
|
||||
0xc000008c,
|
||||
0xc000008d,
|
||||
0xc000008e,
|
||||
0xc000008f,
|
||||
0xc0000090,
|
||||
0xc0000091,
|
||||
0xc0000092,
|
||||
0xc0000093,
|
||||
0xc0000094,
|
||||
0xc0000095,
|
||||
0xc0000096,
|
||||
0xc00000fd,
|
||||
0xc0000135,
|
||||
0xc0000138,
|
||||
0xc0000139,
|
||||
0xc0000142,
|
||||
0xc00002b4,
|
||||
0xc00002b5,
|
||||
0xc0000420,
|
||||
0xc06d007e,
|
||||
0xc06d007f,
|
||||
0xe073616e,
|
||||
0xe0736171,
|
||||
0x0000087a,
|
||||
};
|
||||
|
||||
String8 ctrl_exception_code_kind_display_string_table[38] =
|
||||
{
|
||||
{0},
|
||||
str8_lit_comp("(Win32) Control-C"),
|
||||
str8_lit_comp("(Win32) Control-Break"),
|
||||
str8_lit_comp("(Win32) WinRT Originate Error"),
|
||||
str8_lit_comp("(Win32) WinRT Transform Error"),
|
||||
str8_lit_comp("(Win32) RPC Call Cancelled"),
|
||||
str8_lit_comp("(Win32) Data Type Misalignment"),
|
||||
str8_lit_comp("(Win32) Access Violation"),
|
||||
str8_lit_comp("(Win32) In Page Error"),
|
||||
str8_lit_comp("(Win32) Invalid Handle Specified"),
|
||||
str8_lit_comp("(Win32) Not Enough Quota"),
|
||||
str8_lit_comp("(Win32) Illegal Instruction"),
|
||||
str8_lit_comp("(Win32) Cannot Continue From Exception"),
|
||||
str8_lit_comp("(Win32) Invalid Exception Disposition Returned By Handler"),
|
||||
str8_lit_comp("(Win32) Array Bounds Exceeded"),
|
||||
str8_lit_comp("(Win32) Floating-Point Denormal Operand"),
|
||||
str8_lit_comp("(Win32) Floating-Point Division By Zero"),
|
||||
str8_lit_comp("(Win32) Floating-Point Inexact Result"),
|
||||
str8_lit_comp("(Win32) Floating-Point Invalid Operation"),
|
||||
str8_lit_comp("(Win32) Floating-Point Overflow"),
|
||||
str8_lit_comp("(Win32) Floating-Point Stack Check"),
|
||||
str8_lit_comp("(Win32) Floating-Point Underflow"),
|
||||
str8_lit_comp("(Win32) Integer Division By Zero"),
|
||||
str8_lit_comp("(Win32) Integer Overflow"),
|
||||
str8_lit_comp("(Win32) Privileged Instruction"),
|
||||
str8_lit_comp("(Win32) Stack Overflow"),
|
||||
str8_lit_comp("(Win32) Unable To Locate DLL"),
|
||||
str8_lit_comp("(Win32) Ordinal Not Found"),
|
||||
str8_lit_comp("(Win32) Entry Point Not Found"),
|
||||
str8_lit_comp("(Win32) DLL Initialization Failed"),
|
||||
str8_lit_comp("(Win32) Floating Point SSE Multiple Faults"),
|
||||
str8_lit_comp("(Win32) Floating Point SSE Multiple Traps"),
|
||||
str8_lit_comp("(Win32) Assertion Failed"),
|
||||
str8_lit_comp("(Win32) Module Not Found"),
|
||||
str8_lit_comp("(Win32) Procedure Not Found"),
|
||||
str8_lit_comp("(Win32) Sanitizer Error Detected"),
|
||||
str8_lit_comp("(Win32) Sanitizer Raw Access Violation"),
|
||||
str8_lit_comp("(Win32) DirectX Debug Layer"),
|
||||
};
|
||||
|
||||
String8 ctrl_exception_code_kind_lowercase_code_string_table[38] =
|
||||
{
|
||||
{0},
|
||||
str8_lit_comp("win32_ctrl_c"),
|
||||
str8_lit_comp("win32_ctrl_break"),
|
||||
str8_lit_comp("win32_win_rt_originate_error"),
|
||||
str8_lit_comp("win32_win_rt_transform_error"),
|
||||
str8_lit_comp("win32_rpc_call_cancelled"),
|
||||
str8_lit_comp("win32_datatype_misalignment"),
|
||||
str8_lit_comp("win32_access_violation"),
|
||||
str8_lit_comp("win32_in_page_error"),
|
||||
str8_lit_comp("win32_invalid_handle"),
|
||||
str8_lit_comp("win32_not_enough_quota"),
|
||||
str8_lit_comp("win32_illegal_instruction"),
|
||||
str8_lit_comp("win32_cannot_continue_exception"),
|
||||
str8_lit_comp("win32_invalid_exception_disposition"),
|
||||
str8_lit_comp("win32_array_bounds_exceeded"),
|
||||
str8_lit_comp("win32_floating_point_denormal_operand"),
|
||||
str8_lit_comp("win32_floating_point_division_by_zero"),
|
||||
str8_lit_comp("win32_floating_point_inexact_result"),
|
||||
str8_lit_comp("win32_floating_point_invalid_operation"),
|
||||
str8_lit_comp("win32_floating_point_overflow"),
|
||||
str8_lit_comp("win32_floating_point_stack_check"),
|
||||
str8_lit_comp("win32_floating_point_underflow"),
|
||||
str8_lit_comp("win32_integer_division_by_zero"),
|
||||
str8_lit_comp("win32_integer_overflow"),
|
||||
str8_lit_comp("win32_privileged_instruction"),
|
||||
str8_lit_comp("win32_stack_overflow"),
|
||||
str8_lit_comp("win32_unable_to_locate_dll"),
|
||||
str8_lit_comp("win32_ordinal_not_found"),
|
||||
str8_lit_comp("win32_entry_point_not_found"),
|
||||
str8_lit_comp("win32_dll_initialization_failed"),
|
||||
str8_lit_comp("win32_floating_point_sse_multiple_faults"),
|
||||
str8_lit_comp("win32_floating_point_sse_multiple_traps"),
|
||||
str8_lit_comp("win32_assertion_failed"),
|
||||
str8_lit_comp("win32_module_not_found"),
|
||||
str8_lit_comp("win32_procedure_not_found"),
|
||||
str8_lit_comp("win32_sanitizer_error_detected"),
|
||||
str8_lit_comp("win32_sanitizer_raw_access_violation"),
|
||||
str8_lit_comp("win32_directx_debug_layer"),
|
||||
};
|
||||
|
||||
B8 ctrl_exception_code_kind_default_enable_table[38] =
|
||||
{
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
};
|
||||
|
||||
C_LINKAGE_END
|
||||
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
C_LINKAGE_BEGIN
|
||||
U32 ctrl_exception_code_kind_code_table[38] =
|
||||
{
|
||||
0,
|
||||
0x40010005,
|
||||
0x40010008,
|
||||
0x40080201,
|
||||
0x40080202,
|
||||
0x0000071a,
|
||||
0x80000002,
|
||||
0xc0000005,
|
||||
0xc0000006,
|
||||
0xc0000008,
|
||||
0xc0000017,
|
||||
0xc000001d,
|
||||
0xc0000025,
|
||||
0xc0000026,
|
||||
0xc000008c,
|
||||
0xc000008d,
|
||||
0xc000008e,
|
||||
0xc000008f,
|
||||
0xc0000090,
|
||||
0xc0000091,
|
||||
0xc0000092,
|
||||
0xc0000093,
|
||||
0xc0000094,
|
||||
0xc0000095,
|
||||
0xc0000096,
|
||||
0xc00000fd,
|
||||
0xc0000135,
|
||||
0xc0000138,
|
||||
0xc0000139,
|
||||
0xc0000142,
|
||||
0xc00002b4,
|
||||
0xc00002b5,
|
||||
0xc0000420,
|
||||
0xc06d007e,
|
||||
0xc06d007f,
|
||||
0xe073616e,
|
||||
0xe0736171,
|
||||
0x0000087a,
|
||||
};
|
||||
|
||||
String8 ctrl_exception_code_kind_display_string_table[38] =
|
||||
{
|
||||
{0},
|
||||
str8_lit_comp("(Win32) Control-C"),
|
||||
str8_lit_comp("(Win32) Control-Break"),
|
||||
str8_lit_comp("(Win32) WinRT Originate Error"),
|
||||
str8_lit_comp("(Win32) WinRT Transform Error"),
|
||||
str8_lit_comp("(Win32) RPC Call Cancelled"),
|
||||
str8_lit_comp("(Win32) Data Type Misalignment"),
|
||||
str8_lit_comp("(Win32) Access Violation"),
|
||||
str8_lit_comp("(Win32) In Page Error"),
|
||||
str8_lit_comp("(Win32) Invalid Handle Specified"),
|
||||
str8_lit_comp("(Win32) Not Enough Quota"),
|
||||
str8_lit_comp("(Win32) Illegal Instruction"),
|
||||
str8_lit_comp("(Win32) Cannot Continue From Exception"),
|
||||
str8_lit_comp("(Win32) Invalid Exception Disposition Returned By Handler"),
|
||||
str8_lit_comp("(Win32) Array Bounds Exceeded"),
|
||||
str8_lit_comp("(Win32) Floating-Point Denormal Operand"),
|
||||
str8_lit_comp("(Win32) Floating-Point Division By Zero"),
|
||||
str8_lit_comp("(Win32) Floating-Point Inexact Result"),
|
||||
str8_lit_comp("(Win32) Floating-Point Invalid Operation"),
|
||||
str8_lit_comp("(Win32) Floating-Point Overflow"),
|
||||
str8_lit_comp("(Win32) Floating-Point Stack Check"),
|
||||
str8_lit_comp("(Win32) Floating-Point Underflow"),
|
||||
str8_lit_comp("(Win32) Integer Division By Zero"),
|
||||
str8_lit_comp("(Win32) Integer Overflow"),
|
||||
str8_lit_comp("(Win32) Privileged Instruction"),
|
||||
str8_lit_comp("(Win32) Stack Overflow"),
|
||||
str8_lit_comp("(Win32) Unable To Locate DLL"),
|
||||
str8_lit_comp("(Win32) Ordinal Not Found"),
|
||||
str8_lit_comp("(Win32) Entry Point Not Found"),
|
||||
str8_lit_comp("(Win32) DLL Initialization Failed"),
|
||||
str8_lit_comp("(Win32) Floating Point SSE Multiple Faults"),
|
||||
str8_lit_comp("(Win32) Floating Point SSE Multiple Traps"),
|
||||
str8_lit_comp("(Win32) Assertion Failed"),
|
||||
str8_lit_comp("(Win32) Module Not Found"),
|
||||
str8_lit_comp("(Win32) Procedure Not Found"),
|
||||
str8_lit_comp("(Win32) Sanitizer Error Detected"),
|
||||
str8_lit_comp("(Win32) Sanitizer Raw Access Violation"),
|
||||
str8_lit_comp("(Win32) DirectX Debug Layer"),
|
||||
};
|
||||
|
||||
String8 ctrl_exception_code_kind_lowercase_code_string_table[38] =
|
||||
{
|
||||
{0},
|
||||
str8_lit_comp("win32_ctrl_c"),
|
||||
str8_lit_comp("win32_ctrl_break"),
|
||||
str8_lit_comp("win32_win_rt_originate_error"),
|
||||
str8_lit_comp("win32_win_rt_transform_error"),
|
||||
str8_lit_comp("win32_rpc_call_cancelled"),
|
||||
str8_lit_comp("win32_datatype_misalignment"),
|
||||
str8_lit_comp("win32_access_violation"),
|
||||
str8_lit_comp("win32_in_page_error"),
|
||||
str8_lit_comp("win32_invalid_handle"),
|
||||
str8_lit_comp("win32_not_enough_quota"),
|
||||
str8_lit_comp("win32_illegal_instruction"),
|
||||
str8_lit_comp("win32_cannot_continue_exception"),
|
||||
str8_lit_comp("win32_invalid_exception_disposition"),
|
||||
str8_lit_comp("win32_array_bounds_exceeded"),
|
||||
str8_lit_comp("win32_floating_point_denormal_operand"),
|
||||
str8_lit_comp("win32_floating_point_division_by_zero"),
|
||||
str8_lit_comp("win32_floating_point_inexact_result"),
|
||||
str8_lit_comp("win32_floating_point_invalid_operation"),
|
||||
str8_lit_comp("win32_floating_point_overflow"),
|
||||
str8_lit_comp("win32_floating_point_stack_check"),
|
||||
str8_lit_comp("win32_floating_point_underflow"),
|
||||
str8_lit_comp("win32_integer_division_by_zero"),
|
||||
str8_lit_comp("win32_integer_overflow"),
|
||||
str8_lit_comp("win32_privileged_instruction"),
|
||||
str8_lit_comp("win32_stack_overflow"),
|
||||
str8_lit_comp("win32_unable_to_locate_dll"),
|
||||
str8_lit_comp("win32_ordinal_not_found"),
|
||||
str8_lit_comp("win32_entry_point_not_found"),
|
||||
str8_lit_comp("win32_dll_initialization_failed"),
|
||||
str8_lit_comp("win32_floating_point_sse_multiple_faults"),
|
||||
str8_lit_comp("win32_floating_point_sse_multiple_traps"),
|
||||
str8_lit_comp("win32_assertion_failed"),
|
||||
str8_lit_comp("win32_module_not_found"),
|
||||
str8_lit_comp("win32_procedure_not_found"),
|
||||
str8_lit_comp("win32_sanitizer_error_detected"),
|
||||
str8_lit_comp("win32_sanitizer_raw_access_violation"),
|
||||
str8_lit_comp("win32_directx_debug_layer"),
|
||||
};
|
||||
|
||||
B8 ctrl_exception_code_kind_default_enable_table[38] =
|
||||
{
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
};
|
||||
|
||||
C_LINKAGE_END
|
||||
|
||||
|
||||
@@ -1,79 +1,59 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
#ifndef CTRL_META_H
|
||||
#define CTRL_META_H
|
||||
|
||||
typedef enum CTRL_EntityKind
|
||||
{
|
||||
CTRL_EntityKind_Null,
|
||||
CTRL_EntityKind_Root,
|
||||
CTRL_EntityKind_Machine,
|
||||
CTRL_EntityKind_Process,
|
||||
CTRL_EntityKind_Thread,
|
||||
CTRL_EntityKind_Module,
|
||||
CTRL_EntityKind_EntryPoint,
|
||||
CTRL_EntityKind_DebugInfoPath,
|
||||
CTRL_EntityKind_PendingThreadName,
|
||||
CTRL_EntityKind_PendingThreadColor,
|
||||
CTRL_EntityKind_Breakpoint,
|
||||
CTRL_EntityKind_AddressRangeAnnotation,
|
||||
CTRL_EntityKind_COUNT,
|
||||
} CTRL_EntityKind;
|
||||
|
||||
typedef enum CTRL_ExceptionCodeKind
|
||||
{
|
||||
CTRL_ExceptionCodeKind_Null,
|
||||
CTRL_ExceptionCodeKind_Win32CtrlC,
|
||||
CTRL_ExceptionCodeKind_Win32CtrlBreak,
|
||||
CTRL_ExceptionCodeKind_Win32WinRTOriginateError,
|
||||
CTRL_ExceptionCodeKind_Win32WinRTTransformError,
|
||||
CTRL_ExceptionCodeKind_Win32RPCCallCancelled,
|
||||
CTRL_ExceptionCodeKind_Win32DatatypeMisalignment,
|
||||
CTRL_ExceptionCodeKind_Win32AccessViolation,
|
||||
CTRL_ExceptionCodeKind_Win32InPageError,
|
||||
CTRL_ExceptionCodeKind_Win32InvalidHandle,
|
||||
CTRL_ExceptionCodeKind_Win32NotEnoughQuota,
|
||||
CTRL_ExceptionCodeKind_Win32IllegalInstruction,
|
||||
CTRL_ExceptionCodeKind_Win32CannotContinueException,
|
||||
CTRL_ExceptionCodeKind_Win32InvalidExceptionDisposition,
|
||||
CTRL_ExceptionCodeKind_Win32ArrayBoundsExceeded,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointDenormalOperand,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointDivisionByZero,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointInexactResult,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointInvalidOperation,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointOverflow,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointStackCheck,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointUnderflow,
|
||||
CTRL_ExceptionCodeKind_Win32IntegerDivisionByZero,
|
||||
CTRL_ExceptionCodeKind_Win32IntegerOverflow,
|
||||
CTRL_ExceptionCodeKind_Win32PrivilegedInstruction,
|
||||
CTRL_ExceptionCodeKind_Win32StackOverflow,
|
||||
CTRL_ExceptionCodeKind_Win32UnableToLocateDLL,
|
||||
CTRL_ExceptionCodeKind_Win32OrdinalNotFound,
|
||||
CTRL_ExceptionCodeKind_Win32EntryPointNotFound,
|
||||
CTRL_ExceptionCodeKind_Win32DLLInitializationFailed,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointSSEMultipleFaults,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointSSEMultipleTraps,
|
||||
CTRL_ExceptionCodeKind_Win32AssertionFailed,
|
||||
CTRL_ExceptionCodeKind_Win32ModuleNotFound,
|
||||
CTRL_ExceptionCodeKind_Win32ProcedureNotFound,
|
||||
CTRL_ExceptionCodeKind_Win32SanitizerErrorDetected,
|
||||
CTRL_ExceptionCodeKind_Win32SanitizerRawAccessViolation,
|
||||
CTRL_ExceptionCodeKind_Win32DirectXDebugLayer,
|
||||
CTRL_ExceptionCodeKind_COUNT,
|
||||
} CTRL_ExceptionCodeKind;
|
||||
|
||||
C_LINKAGE_BEGIN
|
||||
extern String8 ctrl_entity_kind_code_name_table[12];
|
||||
extern String8 ctrl_entity_kind_display_string_table[12];
|
||||
extern U32 ctrl_exception_code_kind_code_table[38];
|
||||
extern String8 ctrl_exception_code_kind_display_string_table[38];
|
||||
extern String8 ctrl_exception_code_kind_lowercase_code_string_table[38];
|
||||
extern B8 ctrl_exception_code_kind_default_enable_table[38];
|
||||
|
||||
C_LINKAGE_END
|
||||
|
||||
#endif // CTRL_META_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
#ifndef CTRL_META_H
|
||||
#define CTRL_META_H
|
||||
|
||||
typedef enum CTRL_ExceptionCodeKind
|
||||
{
|
||||
CTRL_ExceptionCodeKind_Null,
|
||||
CTRL_ExceptionCodeKind_Win32CtrlC,
|
||||
CTRL_ExceptionCodeKind_Win32CtrlBreak,
|
||||
CTRL_ExceptionCodeKind_Win32WinRTOriginateError,
|
||||
CTRL_ExceptionCodeKind_Win32WinRTTransformError,
|
||||
CTRL_ExceptionCodeKind_Win32RPCCallCancelled,
|
||||
CTRL_ExceptionCodeKind_Win32DatatypeMisalignment,
|
||||
CTRL_ExceptionCodeKind_Win32AccessViolation,
|
||||
CTRL_ExceptionCodeKind_Win32InPageError,
|
||||
CTRL_ExceptionCodeKind_Win32InvalidHandle,
|
||||
CTRL_ExceptionCodeKind_Win32NotEnoughQuota,
|
||||
CTRL_ExceptionCodeKind_Win32IllegalInstruction,
|
||||
CTRL_ExceptionCodeKind_Win32CannotContinueException,
|
||||
CTRL_ExceptionCodeKind_Win32InvalidExceptionDisposition,
|
||||
CTRL_ExceptionCodeKind_Win32ArrayBoundsExceeded,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointDenormalOperand,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointDivisionByZero,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointInexactResult,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointInvalidOperation,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointOverflow,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointStackCheck,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointUnderflow,
|
||||
CTRL_ExceptionCodeKind_Win32IntegerDivisionByZero,
|
||||
CTRL_ExceptionCodeKind_Win32IntegerOverflow,
|
||||
CTRL_ExceptionCodeKind_Win32PrivilegedInstruction,
|
||||
CTRL_ExceptionCodeKind_Win32StackOverflow,
|
||||
CTRL_ExceptionCodeKind_Win32UnableToLocateDLL,
|
||||
CTRL_ExceptionCodeKind_Win32OrdinalNotFound,
|
||||
CTRL_ExceptionCodeKind_Win32EntryPointNotFound,
|
||||
CTRL_ExceptionCodeKind_Win32DLLInitializationFailed,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointSSEMultipleFaults,
|
||||
CTRL_ExceptionCodeKind_Win32FloatingPointSSEMultipleTraps,
|
||||
CTRL_ExceptionCodeKind_Win32AssertionFailed,
|
||||
CTRL_ExceptionCodeKind_Win32ModuleNotFound,
|
||||
CTRL_ExceptionCodeKind_Win32ProcedureNotFound,
|
||||
CTRL_ExceptionCodeKind_Win32SanitizerErrorDetected,
|
||||
CTRL_ExceptionCodeKind_Win32SanitizerRawAccessViolation,
|
||||
CTRL_ExceptionCodeKind_Win32DirectXDebugLayer,
|
||||
CTRL_ExceptionCodeKind_COUNT,
|
||||
} CTRL_ExceptionCodeKind;
|
||||
|
||||
C_LINKAGE_BEGIN
|
||||
extern U32 ctrl_exception_code_kind_code_table[38];
|
||||
extern String8 ctrl_exception_code_kind_display_string_table[38];
|
||||
extern String8 ctrl_exception_code_kind_lowercase_code_string_table[38];
|
||||
extern B8 ctrl_exception_code_kind_default_enable_table[38];
|
||||
C_LINKAGE_END
|
||||
|
||||
#endif // CTRL_META_H
|
||||
|
||||
@@ -0,0 +1,725 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Third Party Includes
|
||||
|
||||
#include "third_party/udis86/config.h"
|
||||
#include "third_party/udis86/udis86.h"
|
||||
#include "third_party/udis86/libudis86/decode.c"
|
||||
#include "third_party/udis86/libudis86/itab.c"
|
||||
#include "third_party/udis86/libudis86/syn-att.c"
|
||||
#include "third_party/udis86/libudis86/syn-intel.c"
|
||||
#include "third_party/udis86/libudis86/syn.c"
|
||||
#include "third_party/udis86/libudis86/udis86.c"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parameter Type Functions
|
||||
|
||||
internal B32
|
||||
dasm_params_match(DASM_Params *a, DASM_Params *b)
|
||||
{
|
||||
B32 result = (a->vaddr == b->vaddr &&
|
||||
a->arch == b->arch &&
|
||||
a->style_flags == b->style_flags &&
|
||||
a->syntax == b->syntax &&
|
||||
a->base_vaddr == b->base_vaddr &&
|
||||
di_key_match(&a->dbgi_key, &b->dbgi_key));
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Instruction Type Functions
|
||||
|
||||
internal void
|
||||
dasm_inst_chunk_list_push(Arena *arena, DASM_InstChunkList *list, U64 cap, DASM_Inst *inst)
|
||||
{
|
||||
DASM_InstChunkNode *node = list->last;
|
||||
if(node == 0 || node->count >= node->cap)
|
||||
{
|
||||
node = push_array(arena, DASM_InstChunkNode, 1);
|
||||
node->v = push_array_no_zero(arena, DASM_Inst, cap);
|
||||
node->cap = cap;
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->node_count += 1;
|
||||
}
|
||||
MemoryCopyStruct(&node->v[node->count], inst);
|
||||
node->count += 1;
|
||||
list->inst_count += 1;
|
||||
}
|
||||
|
||||
internal DASM_InstArray
|
||||
dasm_inst_array_from_chunk_list(Arena *arena, DASM_InstChunkList *list)
|
||||
{
|
||||
DASM_InstArray array = {0};
|
||||
array.count = list->inst_count;
|
||||
array.v = push_array_no_zero(arena, DASM_Inst, array.count);
|
||||
U64 idx = 0;
|
||||
for(DASM_InstChunkNode *n = list->first; n != 0; n = n->next)
|
||||
{
|
||||
MemoryCopy(array.v+idx, n->v, sizeof(DASM_Inst)*n->count);
|
||||
idx += n->count;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
internal U64
|
||||
dasm_inst_array_idx_from_code_off__linear_scan(DASM_InstArray *array, U64 off)
|
||||
{
|
||||
U64 result = 0;
|
||||
for(U64 idx = 0; idx < array->count; idx += 1)
|
||||
{
|
||||
if(array->v[idx].code_off == off)
|
||||
{
|
||||
result = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
dasm_inst_array_code_off_from_idx(DASM_InstArray *array, U64 idx)
|
||||
{
|
||||
U64 off = 0;
|
||||
if(idx < array->count)
|
||||
{
|
||||
off = array->v[idx].code_off;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void
|
||||
dasm_init(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
dasm_shared = push_array(arena, DASM_Shared, 1);
|
||||
dasm_shared->arena = arena;
|
||||
dasm_shared->slots_count = 1024;
|
||||
dasm_shared->stripes_count = Min(dasm_shared->slots_count, os_logical_core_count());
|
||||
dasm_shared->slots = push_array(arena, DASM_Slot, dasm_shared->slots_count);
|
||||
dasm_shared->stripes = push_array(arena, DASM_Stripe, dasm_shared->stripes_count);
|
||||
for(U64 idx = 0; idx < dasm_shared->stripes_count; idx += 1)
|
||||
{
|
||||
dasm_shared->stripes[idx].arena = arena_alloc();
|
||||
dasm_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
|
||||
dasm_shared->stripes[idx].cv = os_condition_variable_alloc();
|
||||
}
|
||||
dasm_shared->u2p_ring_size = KB(64);
|
||||
dasm_shared->u2p_ring_base = push_array_no_zero(arena, U8, dasm_shared->u2p_ring_size);
|
||||
dasm_shared->u2p_ring_cv = os_condition_variable_alloc();
|
||||
dasm_shared->u2p_ring_mutex = os_mutex_alloc();
|
||||
dasm_shared->parse_thread_count = 1;
|
||||
dasm_shared->parse_threads = push_array(arena, OS_Handle, dasm_shared->parse_thread_count);
|
||||
for(U64 idx = 0; idx < dasm_shared->parse_thread_count; idx += 1)
|
||||
{
|
||||
dasm_shared->parse_threads[idx] = os_launch_thread(dasm_parse_thread__entry_point, (void *)idx, 0);
|
||||
}
|
||||
dasm_shared->evictor_detector_thread = os_launch_thread(dasm_evictor_detector_thread__entry_point, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: User Clock
|
||||
|
||||
internal void
|
||||
dasm_user_clock_tick(void)
|
||||
{
|
||||
ins_atomic_u64_inc_eval(&dasm_shared->user_clock_idx);
|
||||
}
|
||||
|
||||
internal U64
|
||||
dasm_user_clock_idx(void)
|
||||
{
|
||||
U64 idx = ins_atomic_u64_eval(&dasm_shared->user_clock_idx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scoped Access
|
||||
|
||||
internal DASM_Scope *
|
||||
dasm_scope_open(void)
|
||||
{
|
||||
if(dasm_tctx == 0)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
dasm_tctx = push_array(arena, DASM_TCTX, 1);
|
||||
dasm_tctx->arena = arena;
|
||||
}
|
||||
U64 base_pos = arena_pos(dasm_tctx->arena);
|
||||
DASM_Scope *scope = push_array(dasm_tctx->arena, DASM_Scope, 1);
|
||||
scope->base_pos = base_pos;
|
||||
return scope;
|
||||
}
|
||||
|
||||
internal void
|
||||
dasm_scope_close(DASM_Scope *scope)
|
||||
{
|
||||
for(DASM_Touch *t = scope->top_touch, *next = 0; t != 0; t = next)
|
||||
{
|
||||
next = t->next;
|
||||
U64 slot_idx = t->hash.u64[1]%dasm_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
|
||||
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
|
||||
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(t->hash, n->hash) && dasm_params_match(&t->params, &n->params))
|
||||
{
|
||||
ins_atomic_u64_dec_eval(&n->scope_ref_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
arena_pop_to(dasm_tctx->arena, scope->base_pos);
|
||||
}
|
||||
|
||||
internal void
|
||||
dasm_scope_touch_node__stripe_r_guarded(DASM_Scope *scope, DASM_Node *node)
|
||||
{
|
||||
DASM_Touch *touch = push_array(dasm_tctx->arena, DASM_Touch, 1);
|
||||
ins_atomic_u64_inc_eval(&node->scope_ref_count);
|
||||
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
|
||||
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, dasm_user_clock_idx());
|
||||
touch->hash = node->hash;
|
||||
MemoryCopyStruct(&touch->params, &node->params);
|
||||
touch->params.dbgi_key = di_key_copy(dasm_tctx->arena, &touch->params.dbgi_key);
|
||||
SLLStackPush(scope->top_touch, touch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal DASM_Info
|
||||
dasm_info_from_hash_params(DASM_Scope *scope, U128 hash, DASM_Params *params)
|
||||
{
|
||||
DASM_Info info = {0};
|
||||
if(!u128_match(hash, u128_zero()))
|
||||
{
|
||||
U64 slot_idx = hash.u64[1]%dasm_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
|
||||
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
|
||||
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
|
||||
B32 found = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(hash, n->hash) && dasm_params_match(params, &n->params))
|
||||
{
|
||||
MemoryCopyStruct(&info, &n->info);
|
||||
found = 1;
|
||||
dasm_scope_touch_node__stripe_r_guarded(scope, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
B32 node_is_new = 0;
|
||||
if(!found)
|
||||
{
|
||||
OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
DASM_Node *node = 0;
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(hash, n->hash) && dasm_params_match(params, &n->params))
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(node == 0)
|
||||
{
|
||||
log_infof("[dasm] cache miss, creating node...\n");
|
||||
log_infof(" hash: [0x%I64x 0x%I64x]\n", hash.u64[0], hash.u64[1]);
|
||||
log_infof(" vaddr: 0x%I64x\n", params->vaddr);
|
||||
log_infof(" arch: %S\n", string_from_architecture(params->arch));
|
||||
log_infof(" style_flags: 0x%x\n", params->style_flags);
|
||||
log_infof(" syntax: %i\n", params->syntax);
|
||||
log_infof(" base_vaddr: 0x%I64x\n", params->base_vaddr);
|
||||
log_infof(" dbgi_key: [%S 0x%I64x]\n", params->dbgi_key.path, params->dbgi_key.min_timestamp);
|
||||
node = stripe->free_node;
|
||||
if(node)
|
||||
{
|
||||
SLLStackPop(stripe->free_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = push_array_no_zero(stripe->arena, DASM_Node, 1);
|
||||
}
|
||||
MemoryZeroStruct(node);
|
||||
DLLPushBack(slot->first, slot->last, node);
|
||||
node->hash = hash;
|
||||
MemoryCopyStruct(&node->params, params);
|
||||
// TODO(rjf): need to make this releasable - currently all exe_paths just leak
|
||||
node->params.dbgi_key = di_key_copy(stripe->arena, &node->params.dbgi_key);
|
||||
node_is_new = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(node_is_new)
|
||||
{
|
||||
dasm_u2p_enqueue_req(hash, params, max_U64);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
internal DASM_Info
|
||||
dasm_info_from_key_params(DASM_Scope *scope, U128 key, DASM_Params *params, U128 *hash_out)
|
||||
{
|
||||
DASM_Info result = {0};
|
||||
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
|
||||
{
|
||||
U128 hash = hs_hash_from_key(key, rewind_idx);
|
||||
result = dasm_info_from_hash_params(scope, hash, params);
|
||||
if(result.insts.count != 0)
|
||||
{
|
||||
if(hash_out)
|
||||
{
|
||||
*hash_out = hash;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parse Threads
|
||||
|
||||
internal B32
|
||||
dasm_u2p_enqueue_req(U128 hash, DASM_Params *params, U64 endt_us)
|
||||
{
|
||||
B32 good = 0;
|
||||
OS_MutexScope(dasm_shared->u2p_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = dasm_shared->u2p_ring_write_pos - dasm_shared->u2p_ring_read_pos;
|
||||
U64 available_size = dasm_shared->u2p_ring_size - unconsumed_size;
|
||||
if(available_size >= sizeof(hash)+sizeof(U64)+sizeof(Architecture)+sizeof(DASM_StyleFlags)+sizeof(DASM_Syntax)+sizeof(U64)+sizeof(U64)+params->dbgi_key.path.size+sizeof(U64))
|
||||
{
|
||||
good = 1;
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &hash);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, ¶ms->vaddr);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, ¶ms->arch);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, ¶ms->style_flags);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, ¶ms->syntax);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, ¶ms->base_vaddr);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, ¶ms->dbgi_key.path.size);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, params->dbgi_key.path.str, params->dbgi_key.path.size);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, ¶ms->dbgi_key.min_timestamp);
|
||||
dasm_shared->u2p_ring_write_pos += 7;
|
||||
dasm_shared->u2p_ring_write_pos -= dasm_shared->u2p_ring_write_pos%8;
|
||||
break;
|
||||
}
|
||||
if(os_now_microseconds() >= endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(dasm_shared->u2p_ring_cv, dasm_shared->u2p_ring_mutex, endt_us);
|
||||
}
|
||||
if(good)
|
||||
{
|
||||
os_condition_variable_broadcast(dasm_shared->u2p_ring_cv);
|
||||
}
|
||||
return good;
|
||||
}
|
||||
|
||||
internal void
|
||||
dasm_u2p_dequeue_req(Arena *arena, U128 *hash_out, DASM_Params *params_out)
|
||||
{
|
||||
OS_MutexScope(dasm_shared->u2p_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = dasm_shared->u2p_ring_write_pos - dasm_shared->u2p_ring_read_pos;
|
||||
if(unconsumed_size >= sizeof(*hash_out)+sizeof(U64)+sizeof(Architecture)+sizeof(DASM_StyleFlags)+sizeof(DASM_Syntax)+sizeof(U64)+sizeof(U64)+sizeof(U64))
|
||||
{
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, hash_out);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, ¶ms_out->vaddr);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, ¶ms_out->arch);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, ¶ms_out->style_flags);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, ¶ms_out->syntax);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, ¶ms_out->base_vaddr);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, ¶ms_out->dbgi_key.path.size);
|
||||
params_out->dbgi_key.path.str = push_array(arena, U8, params_out->dbgi_key.path.size);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, params_out->dbgi_key.path.str, params_out->dbgi_key.path.size);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, ¶ms_out->dbgi_key.min_timestamp);
|
||||
dasm_shared->u2p_ring_read_pos += 7;
|
||||
dasm_shared->u2p_ring_read_pos -= dasm_shared->u2p_ring_read_pos%8;
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(dasm_shared->u2p_ring_cv, dasm_shared->u2p_ring_mutex, max_U64);
|
||||
}
|
||||
os_condition_variable_broadcast(dasm_shared->u2p_ring_mutex);
|
||||
}
|
||||
|
||||
internal void
|
||||
dasm_parse_thread__entry_point(void *p)
|
||||
{
|
||||
ThreadNameF("[dasm] parse thread #%I64u", (U64)p);
|
||||
for(;;)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
//- rjf: get next request
|
||||
U128 hash = {0};
|
||||
DASM_Params params = {0};
|
||||
dasm_u2p_dequeue_req(scratch.arena, &hash, ¶ms);
|
||||
U64 change_gen = fs_change_gen();
|
||||
HS_Scope *hs_scope = hs_scope_open();
|
||||
DI_Scope *di_scope = di_scope_open();
|
||||
TXT_Scope *txt_scope = txt_scope_open();
|
||||
|
||||
//- rjf: unpack hash
|
||||
U64 slot_idx = hash.u64[1]%dasm_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
|
||||
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
|
||||
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
|
||||
|
||||
//- rjf: take task
|
||||
B32 got_task = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(n->hash, hash) && dasm_params_match(&n->params, ¶ms))
|
||||
{
|
||||
got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: get dbg info
|
||||
RDI_Parsed *rdi = &di_rdi_parsed_nil;
|
||||
if(got_task && params.dbgi_key.path.size != 0)
|
||||
{
|
||||
rdi = di_rdi_from_key(di_scope, ¶ms.dbgi_key, max_U64);
|
||||
}
|
||||
|
||||
//- rjf: hash -> data
|
||||
String8 data = {0};
|
||||
if(got_task)
|
||||
{
|
||||
data = hs_data_from_hash(hs_scope, hash);
|
||||
}
|
||||
|
||||
//- rjf: data * arch * addr * dbg -> decode artifacts
|
||||
DASM_InstChunkList inst_list = {0};
|
||||
String8List inst_strings = {0};
|
||||
if(got_task)
|
||||
{
|
||||
switch(params.arch)
|
||||
{
|
||||
default:{}break;
|
||||
|
||||
//- rjf: x86/x64 decoding
|
||||
case Architecture_x64:
|
||||
case Architecture_x86:
|
||||
{
|
||||
// rjf: grab context
|
||||
struct ud udc;
|
||||
ud_init(&udc);
|
||||
ud_set_mode(&udc, bit_size_from_arch(params.arch));
|
||||
ud_set_pc(&udc, params.vaddr);
|
||||
ud_set_input_buffer(&udc, data.str, data.size);
|
||||
ud_set_vendor(&udc, UD_VENDOR_ANY);
|
||||
ud_set_syntax(&udc, params.syntax == DASM_Syntax_Intel ? UD_SYN_INTEL : UD_SYN_ATT);
|
||||
|
||||
// rjf: disassemble
|
||||
RDI_SourceFile *last_file = &rdi_source_file_nil;
|
||||
RDI_Line *last_line = 0;
|
||||
for(U64 off = 0; off < data.size;)
|
||||
{
|
||||
// rjf: disassemble one instruction
|
||||
U64 size = ud_disassemble(&udc);
|
||||
if(size == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// rjf: analyze
|
||||
struct ud_operand *first_op = (struct ud_operand *)ud_insn_opr(&udc, 0);
|
||||
U64 rel_voff = (first_op != 0 && first_op->type == UD_OP_JIMM) ? ud_syn_rel_target(&udc, first_op) : 0;
|
||||
U64 jump_dst_vaddr = rel_voff;
|
||||
|
||||
// rjf: push strings derived from voff -> line info
|
||||
if(params.style_flags & (DASM_StyleFlag_SourceFilesNames|DASM_StyleFlag_SourceLines))
|
||||
{
|
||||
if(rdi != &di_rdi_parsed_nil)
|
||||
{
|
||||
U64 voff = (params.vaddr+off) - params.base_vaddr;
|
||||
U32 unit_idx = rdi_vmap_idx_from_voff(rdi->unit_vmap, rdi->unit_vmap_count, voff);
|
||||
RDI_Unit *unit = rdi_element_from_idx(rdi, units, unit_idx);
|
||||
RDI_ParsedLineInfo unit_line_info = {0};
|
||||
rdi_line_info_from_unit(rdi, unit, &unit_line_info);
|
||||
U64 line_info_idx = rdi_line_info_idx_from_voff(&unit_line_info, voff);
|
||||
if(line_info_idx < unit_line_info.count)
|
||||
{
|
||||
RDI_Line *line = &unit_line_info.lines[line_info_idx];
|
||||
RDI_SourceFile *file = rdi_element_from_idx(rdi, source_files, line->file_idx);
|
||||
String8 file_normalized_full_path = {0};
|
||||
file_normalized_full_path.str = rdi_string_from_idx(rdi, file->normal_full_path_string_idx, &file_normalized_full_path.size);
|
||||
if(file != last_file)
|
||||
{
|
||||
if(params.style_flags & DASM_StyleFlag_SourceFilesNames &&
|
||||
file->normal_full_path_string_idx != 0 && file_normalized_full_path.size != 0)
|
||||
{
|
||||
DASM_Inst inst = {0};
|
||||
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
|
||||
str8_list_pushf(scratch.arena, &inst_strings, "> %S", file_normalized_full_path);
|
||||
}
|
||||
if(params.style_flags & DASM_StyleFlag_SourceFilesNames && file->normal_full_path_string_idx == 0)
|
||||
{
|
||||
DASM_Inst inst = {0};
|
||||
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
|
||||
str8_list_pushf(scratch.arena, &inst_strings, ">");
|
||||
}
|
||||
last_file = file;
|
||||
}
|
||||
if(line && line != last_line && file->normal_full_path_string_idx != 0 &&
|
||||
params.style_flags & DASM_StyleFlag_SourceLines &&
|
||||
file_normalized_full_path.size != 0)
|
||||
{
|
||||
FileProperties props = os_properties_from_file_path(file_normalized_full_path);
|
||||
if(props.modified != 0)
|
||||
{
|
||||
// TODO(rjf): need redirection path - this may map to a different path on the local machine,
|
||||
// need frontend to communicate path remapping info to this layer
|
||||
U128 key = fs_key_from_path(file_normalized_full_path);
|
||||
TXT_LangKind lang_kind = txt_lang_kind_from_extension(file_normalized_full_path);
|
||||
U64 endt_us = max_U64;
|
||||
U128 hash = {0};
|
||||
TXT_TextInfo text_info = {0};
|
||||
for(;os_now_microseconds() <= endt_us;)
|
||||
{
|
||||
text_info = txt_text_info_from_key_lang(txt_scope, key, lang_kind, &hash);
|
||||
if(!u128_match(hash, u128_zero()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(0 < line->line_num && line->line_num < text_info.lines_count)
|
||||
{
|
||||
String8 data = hs_data_from_hash(hs_scope, hash);
|
||||
String8 line_text = str8_skip_chop_whitespace(str8_substr(data, text_info.lines_ranges[line->line_num-1]));
|
||||
if(line_text.size != 0)
|
||||
{
|
||||
DASM_Inst inst = {0};
|
||||
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
|
||||
str8_list_pushf(scratch.arena, &inst_strings, "> %S", line_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
last_line = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: push
|
||||
String8 addr_part = {0};
|
||||
if(params.style_flags & DASM_StyleFlag_Addresses)
|
||||
{
|
||||
addr_part = push_str8f(scratch.arena, "%s%016I64X ", rdi != &di_rdi_parsed_nil ? " " : "", params.vaddr+off);
|
||||
}
|
||||
String8 code_bytes_part = {0};
|
||||
if(params.style_flags & DASM_StyleFlag_CodeBytes)
|
||||
{
|
||||
String8List code_bytes_strings = {0};
|
||||
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit("{"));
|
||||
for(U64 byte_idx = 0; byte_idx < size || byte_idx < 16; byte_idx += 1)
|
||||
{
|
||||
if(byte_idx < size)
|
||||
{
|
||||
str8_list_pushf(scratch.arena, &code_bytes_strings, "%02x%s ", (U32)data.str[off+byte_idx], byte_idx == size-1 ? "}" : "");
|
||||
}
|
||||
else if(byte_idx < 8)
|
||||
{
|
||||
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit(" "));
|
||||
}
|
||||
}
|
||||
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit(" "));
|
||||
code_bytes_part = str8_list_join(scratch.arena, &code_bytes_strings, 0);
|
||||
}
|
||||
String8 symbol_part = {0};
|
||||
if(jump_dst_vaddr != 0 && rdi != &di_rdi_parsed_nil && params.style_flags & DASM_StyleFlag_SymbolNames)
|
||||
{
|
||||
RDI_U32 scope_idx = rdi_vmap_idx_from_voff(rdi->scope_vmap, rdi->scope_vmap_count, jump_dst_vaddr-params.base_vaddr);
|
||||
if(scope_idx != 0)
|
||||
{
|
||||
RDI_Scope *scope = rdi_element_from_idx(rdi, scopes, scope_idx);
|
||||
RDI_U32 procedure_idx = scope->proc_idx;
|
||||
RDI_Procedure *procedure = rdi_element_from_idx(rdi, procedures, procedure_idx);
|
||||
String8 procedure_name = {0};
|
||||
procedure_name.str = rdi_string_from_idx(rdi, procedure->name_string_idx, &procedure_name.size);
|
||||
if(procedure_name.size != 0)
|
||||
{
|
||||
symbol_part = push_str8f(scratch.arena, " (%S)", procedure_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
String8 inst_string = push_str8f(scratch.arena, "%S%S%s%S", addr_part, code_bytes_part, udc.asm_buf, symbol_part);
|
||||
DASM_Inst inst = {off, rel_voff, r1u64(inst_strings.total_size + inst_strings.node_count,
|
||||
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
|
||||
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
|
||||
str8_list_push(scratch.arena, &inst_strings, inst_string);
|
||||
|
||||
// rjf: increment
|
||||
off += size;
|
||||
}
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: artifacts -> value bundle
|
||||
Arena *info_arena = 0;
|
||||
DASM_Info info = {0};
|
||||
if(got_task)
|
||||
{
|
||||
//- rjf: produce joined text
|
||||
Arena *text_arena = arena_alloc();
|
||||
StringJoin text_join = {0};
|
||||
text_join.sep = str8_lit("\n");
|
||||
String8 text = str8_list_join(text_arena, &inst_strings, &text_join);
|
||||
|
||||
//- rjf: produce unique key for this disassembly's text
|
||||
U128 text_key = {0};
|
||||
{
|
||||
U64 hash_data[] =
|
||||
{
|
||||
hash.u64[0],
|
||||
hash.u64[1],
|
||||
params.vaddr,
|
||||
(U64)params.arch,
|
||||
(U64)params.style_flags,
|
||||
(U64)params.syntax,
|
||||
(U64)rdi,
|
||||
0x4d534144,
|
||||
};
|
||||
text_key = hs_hash_from_data(str8((U8 *)hash_data, sizeof(hash_data)));
|
||||
}
|
||||
|
||||
//- rjf: submit text data to hash store
|
||||
U128 text_hash = hs_submit_data(text_key, &text_arena, text);
|
||||
|
||||
//- rjf: produce value bundle
|
||||
info_arena = arena_alloc();
|
||||
info.text_key = text_key;
|
||||
info.insts = dasm_inst_array_from_chunk_list(info_arena, &inst_list);
|
||||
}
|
||||
|
||||
//- rjf: commit results to cache
|
||||
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(n->hash, hash) && dasm_params_match(&n->params, ¶ms))
|
||||
{
|
||||
n->info_arena = info_arena;
|
||||
MemoryCopyStruct(&n->info, &info);
|
||||
if(rdi != &di_rdi_parsed_nil && params.style_flags & (DASM_StyleFlag_SourceLines|DASM_StyleFlag_SourceFilesNames))
|
||||
{
|
||||
n->change_gen = change_gen;
|
||||
}
|
||||
else
|
||||
{
|
||||
n->change_gen = 0;
|
||||
}
|
||||
ins_atomic_u32_eval_assign(&n->is_working, 0);
|
||||
ins_atomic_u64_inc_eval(&n->load_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
txt_scope_close(txt_scope);
|
||||
di_scope_close(di_scope);
|
||||
hs_scope_close(hs_scope);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Evictor/Detector Thread
|
||||
|
||||
internal void
|
||||
dasm_evictor_detector_thread__entry_point(void *p)
|
||||
{
|
||||
ThreadNameF("[dasm] evictor/detector thread");
|
||||
for(;;)
|
||||
{
|
||||
U64 change_gen = fs_change_gen();
|
||||
U64 check_time_us = os_now_microseconds();
|
||||
U64 check_time_user_clocks = dasm_user_clock_idx();
|
||||
U64 evict_threshold_us = 10*1000000;
|
||||
U64 retry_threshold_us = 1*1000000;
|
||||
U64 evict_threshold_user_clocks = 10;
|
||||
U64 retry_threshold_user_clocks = 10;
|
||||
for(U64 slot_idx = 0; slot_idx < dasm_shared->slots_count; slot_idx += 1)
|
||||
{
|
||||
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
|
||||
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
|
||||
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
|
||||
B32 slot_has_work = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(n->scope_ref_count == 0 &&
|
||||
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
|
||||
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
|
||||
n->load_count != 0 &&
|
||||
n->is_working == 0)
|
||||
{
|
||||
slot_has_work = 1;
|
||||
break;
|
||||
}
|
||||
if(n->change_gen != 0 && n->change_gen != change_gen &&
|
||||
n->last_time_requested_us+retry_threshold_us <= check_time_us &&
|
||||
n->last_user_clock_idx_requested+retry_threshold_user_clocks <= check_time_user_clocks)
|
||||
{
|
||||
slot_has_work = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first, *next = 0; n != 0; n = next)
|
||||
{
|
||||
next = n->next;
|
||||
if(n->scope_ref_count == 0 &&
|
||||
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
|
||||
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
|
||||
n->load_count != 0 &&
|
||||
n->is_working == 0)
|
||||
{
|
||||
DLLRemove(slot->first, slot->last, n);
|
||||
if(n->info_arena != 0)
|
||||
{
|
||||
arena_release(n->info_arena);
|
||||
}
|
||||
SLLStackPush(stripe->free_node, n);
|
||||
}
|
||||
if(n->change_gen != 0 && n->change_gen != change_gen &&
|
||||
n->last_time_requested_us+retry_threshold_us <= check_time_us &&
|
||||
n->last_user_clock_idx_requested+retry_threshold_user_clocks <= check_time_user_clocks)
|
||||
{
|
||||
if(dasm_u2p_enqueue_req(n->hash, &n->params, max_U64))
|
||||
{
|
||||
n->last_time_requested_us = os_now_microseconds();
|
||||
n->last_user_clock_idx_requested = check_time_user_clocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
os_sleep_milliseconds(100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DASM_CACHE_H
|
||||
#define DASM_CACHE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Stringification Types
|
||||
|
||||
typedef U32 DASM_StyleFlags;
|
||||
enum
|
||||
{
|
||||
DASM_StyleFlag_Addresses = (1<<0),
|
||||
DASM_StyleFlag_CodeBytes = (1<<1),
|
||||
DASM_StyleFlag_SourceFilesNames = (1<<2),
|
||||
DASM_StyleFlag_SourceLines = (1<<3),
|
||||
DASM_StyleFlag_SymbolNames = (1<<4),
|
||||
};
|
||||
|
||||
typedef enum DASM_Syntax
|
||||
{
|
||||
DASM_Syntax_Intel,
|
||||
DASM_Syntax_ATT,
|
||||
DASM_Syntax_COUNT
|
||||
}
|
||||
DASM_Syntax;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Disassembling Parameters Bundle
|
||||
|
||||
typedef struct DASM_Params DASM_Params;
|
||||
struct DASM_Params
|
||||
{
|
||||
U64 vaddr;
|
||||
Architecture arch;
|
||||
DASM_StyleFlags style_flags;
|
||||
DASM_Syntax syntax;
|
||||
U64 base_vaddr;
|
||||
DI_Key dbgi_key;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Instruction Types
|
||||
|
||||
typedef struct DASM_Inst DASM_Inst;
|
||||
struct DASM_Inst
|
||||
{
|
||||
U64 code_off;
|
||||
U64 addr;
|
||||
Rng1U64 text_range;
|
||||
};
|
||||
|
||||
typedef struct DASM_InstChunkNode DASM_InstChunkNode;
|
||||
struct DASM_InstChunkNode
|
||||
{
|
||||
DASM_InstChunkNode *next;
|
||||
DASM_Inst *v;
|
||||
U64 cap;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DASM_InstChunkList DASM_InstChunkList;
|
||||
struct DASM_InstChunkList
|
||||
{
|
||||
DASM_InstChunkNode *first;
|
||||
DASM_InstChunkNode *last;
|
||||
U64 node_count;
|
||||
U64 inst_count;
|
||||
};
|
||||
|
||||
typedef struct DASM_InstArray DASM_InstArray;
|
||||
struct DASM_InstArray
|
||||
{
|
||||
DASM_Inst *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Value Bundle Type
|
||||
|
||||
typedef struct DASM_Info DASM_Info;
|
||||
struct DASM_Info
|
||||
{
|
||||
U128 text_key;
|
||||
DASM_InstArray insts;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Types
|
||||
|
||||
typedef struct DASM_Node DASM_Node;
|
||||
struct DASM_Node
|
||||
{
|
||||
// rjf: links
|
||||
DASM_Node *next;
|
||||
DASM_Node *prev;
|
||||
|
||||
// rjf: key
|
||||
U128 hash;
|
||||
DASM_Params params;
|
||||
|
||||
// rjf: generations
|
||||
U64 change_gen;
|
||||
|
||||
// rjf: value
|
||||
Arena *info_arena;
|
||||
DASM_Info info;
|
||||
|
||||
// rjf: metadata
|
||||
B32 is_working;
|
||||
U64 scope_ref_count;
|
||||
U64 last_time_touched_us;
|
||||
U64 last_user_clock_idx_touched;
|
||||
U64 load_count;
|
||||
U64 last_time_requested_us;
|
||||
U64 last_user_clock_idx_requested;
|
||||
};
|
||||
|
||||
typedef struct DASM_Slot DASM_Slot;
|
||||
struct DASM_Slot
|
||||
{
|
||||
DASM_Node *first;
|
||||
DASM_Node *last;
|
||||
};
|
||||
|
||||
typedef struct DASM_Stripe DASM_Stripe;
|
||||
struct DASM_Stripe
|
||||
{
|
||||
Arena *arena;
|
||||
OS_Handle rw_mutex;
|
||||
OS_Handle cv;
|
||||
DASM_Node *free_node;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scoped Access Types
|
||||
|
||||
typedef struct DASM_Touch DASM_Touch;
|
||||
struct DASM_Touch
|
||||
{
|
||||
DASM_Touch *next;
|
||||
U128 hash;
|
||||
DASM_Params params;
|
||||
};
|
||||
|
||||
typedef struct DASM_Scope DASM_Scope;
|
||||
struct DASM_Scope
|
||||
{
|
||||
DASM_Scope *next;
|
||||
DASM_Touch *top_touch;
|
||||
U64 base_pos;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Context
|
||||
|
||||
typedef struct DASM_TCTX DASM_TCTX;
|
||||
struct DASM_TCTX
|
||||
{
|
||||
Arena *arena;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Shared State
|
||||
|
||||
typedef struct DASM_Shared DASM_Shared;
|
||||
struct DASM_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
|
||||
// rjf: user clock
|
||||
U64 user_clock_idx;
|
||||
|
||||
// rjf: cache
|
||||
U64 slots_count;
|
||||
U64 stripes_count;
|
||||
DASM_Slot *slots;
|
||||
DASM_Stripe *stripes;
|
||||
|
||||
// rjf: user -> parse thread
|
||||
U64 u2p_ring_size;
|
||||
U8 *u2p_ring_base;
|
||||
U64 u2p_ring_write_pos;
|
||||
U64 u2p_ring_read_pos;
|
||||
OS_Handle u2p_ring_cv;
|
||||
OS_Handle u2p_ring_mutex;
|
||||
|
||||
// rjf: parse threads
|
||||
U64 parse_thread_count;
|
||||
OS_Handle *parse_threads;
|
||||
|
||||
// rjf: evictor/detector thread
|
||||
OS_Handle evictor_detector_thread;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
thread_static DASM_TCTX *dasm_tctx = 0;
|
||||
global DASM_Shared *dasm_shared = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parameter Type Functions
|
||||
|
||||
internal B32 dasm_params_match(DASM_Params *a, DASM_Params *b);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Instruction Type Functions
|
||||
|
||||
internal void dasm_inst_chunk_list_push(Arena *arena, DASM_InstChunkList *list, U64 cap, DASM_Inst *inst);
|
||||
internal DASM_InstArray dasm_inst_array_from_chunk_list(Arena *arena, DASM_InstChunkList *list);
|
||||
internal U64 dasm_inst_array_idx_from_code_off__linear_scan(DASM_InstArray *array, U64 off);
|
||||
internal U64 dasm_inst_array_code_off_from_idx(DASM_InstArray *array, U64 idx);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void dasm_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: User Clock
|
||||
|
||||
internal void dasm_user_clock_tick(void);
|
||||
internal U64 dasm_user_clock_idx(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scoped Access
|
||||
|
||||
internal DASM_Scope *dasm_scope_open(void);
|
||||
internal void dasm_scope_close(DASM_Scope *scope);
|
||||
internal void dasm_scope_touch_node__stripe_r_guarded(DASM_Scope *scope, DASM_Node *node);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal DASM_Info dasm_info_from_hash_params(DASM_Scope *scope, U128 hash, DASM_Params *params);
|
||||
internal DASM_Info dasm_info_from_key_params(DASM_Scope *scope, U128 key, DASM_Params *params, U128 *hash_out);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parse Threads
|
||||
|
||||
internal B32 dasm_u2p_enqueue_req(U128 hash, DASM_Params *params, U64 endt_us);
|
||||
internal void dasm_u2p_dequeue_req(Arena *arena, U128 *hash_out, DASM_Params *params_out);
|
||||
internal void dasm_parse_thread__entry_point(void *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Evictor/Detector Thread
|
||||
|
||||
internal void dasm_evictor_detector_thread__entry_point(void *p);
|
||||
|
||||
#endif // DASM_CACHE_H
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Built-In Command Tables
|
||||
|
||||
@table(name ui_vis ipc_docs_vis text_pt_vis text_rng_vis q_expr q_slot q_view q_ent_kind q_ctrl_ent_kind q_allow_files q_allow_folders q_keep_oi q_select_oi q_is_code q_floating q_required canonical_icon string display_name desc search_tags ctx_filter)
|
||||
// / | | | | | | \___ _________________________________/ | | | | | |
|
||||
// / | | | | | | \ / | | | | | |
|
||||
D_CmdTable: // | | | | | | | | | | | | | |
|
||||
{
|
||||
//- rjf: low-level target control operations
|
||||
{LaunchAndRun 1 1 0 0 "query:targets" Cfg null Target Null 0 0 0 0 0 1 1 Play "launch_and_run" "Launch and Run" "Starts debugging a new instance of a target, then runs." "launch,start,run,target" "" }
|
||||
{LaunchAndStepInto 1 1 0 0 "query:targets" Cfg null Target Null 0 0 0 0 0 1 1 PlayStepForward "launch_and_step_into" "Launch and Step Into" "Starts debugging a new instance of a target, then stops at the program's entry point." "launch,start,entry,point" "" }
|
||||
{Kill 1 1 0 0 "query:processes" Process null Nil Process 0 0 0 0 0 1 1 X "kill" "Kill" "Kills the specified existing attached process(es)." "stop,kill" "" }
|
||||
{KillAll 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Stop "kill_all" "Kill All" "Kills all attached processes." "stop,kill,all" "" }
|
||||
{Detach 1 1 0 0 "query:processes" Process null Nil Process 0 0 0 0 0 1 1 Null "detach" "Detach" "Detaches the specified attached process(es)." "detach" "" }
|
||||
{Continue 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Play "continue" "Continue" "Continues executing all attached processes." "" "" }
|
||||
{StepIntoInst 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepInto "step_into_inst" "Step Into (Assembly)" "Performs a step that goes into calls, at the instruction level." "single,step,thread" "" }
|
||||
{StepOverInst 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepOver "step_over_inst" "Step Over (Assembly)" "Performs a step that skips calls, at the instruction level." "single,step,thread" "" }
|
||||
{StepIntoLine 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepInto "step_into_line" "Step Into (Line)" "Performs a step that goes into calls, at the source code line level." "step,thread" "" }
|
||||
{StepOverLine 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepOver "step_over_line" "Step Over (Line)" "Performs a step that skips calls, at the source code line level." "step,thread" "" }
|
||||
{StepOut 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepOut "step_out" "Step Out" "Runs to the end of the current function and exits it." "" "" }
|
||||
{Halt 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Pause "halt" "Halt" "Halts all attached processes." "pause" "" }
|
||||
{SoftHaltRefresh 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Refresh "soft_halt_refresh" "Soft Halt Refresh" "Interrupts all attached processes to collect data, and then resumes them." "" "" }
|
||||
{SetThreadIP 0 1 0 0 "" Vaddr null Nil Null 0 0 0 0 1 1 1 Null "set_thread_ip" "Set Thread IP" "Sets the specified thread's instruction pointer at the specified address." "" "" }
|
||||
|
||||
//- rjf: high-level composite target control operations
|
||||
{RunToLine 1 1 1 0 "" Null null Nil Null 0 0 0 0 0 0 0 Play "run_to_line" "Run To Line" "Runs until a particular source line is hit." "" "$text_pt," }
|
||||
{Run 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Play "run" "Run" "Runs all targets after starting them if they have not been started yet." "play" "" }
|
||||
{Restart 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Redo "restart" "Restart" "Kills all attached processes, then launches all active targets." "restart,retry" "" }
|
||||
{StepInto 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepInto "step_into" "Step Into" "Steps once, possibly into function calls, for either source lines or instructions (whichever is selected)." "" "" }
|
||||
{StepOver 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepOver "step_over" "Step Over" "Steps once, always over function calls, for either source lines or instructions." "" "" }
|
||||
|
||||
//- rjf: debug control context management operations
|
||||
{FreezeThread 1 1 0 0 "query:threads" Thread null Nil Thread 0 0 0 0 0 1 1 Locked "freeze_thread" "Freeze Thread" "Freezes the passed thread." "callstack,unwind" "" }
|
||||
{ThawThread 1 1 0 0 "query:threads" Thread null Nil Thread 0 0 0 0 0 1 1 Unlocked "thaw_thread" "Thaw Thread" "Thaws the passed thread." "" "" }
|
||||
{FreezeProcess 1 1 0 0 "query:processes" Process null Nil Process 0 0 0 0 0 1 1 Locked "freeze_process" "Freeze Process" "Freezes the passed process." "" "" }
|
||||
{ThawProcess 1 1 0 0 "query:processes" Process null Nil Process 0 0 0 0 0 1 1 Unlocked "thaw_process" "Thaw Process" "Thaws the passed process." "" "" }
|
||||
{FreezeMachine 0 1 0 0 "query:machines" Machine null Nil Machine 0 0 0 0 0 1 1 Locked "freeze_machine" "Freeze Machine" "Freezes the passed machine." "" "" }
|
||||
{ThawMachine 0 1 0 0 "query:machines" Machine null Nil Machine 0 0 0 0 0 1 1 Unlocked "thaw_machine" "Thaw Machine" "Thaws the passed machine." "" "" }
|
||||
{FreezeLocalMachine 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Machine "freeze_local_machine" "Freeze Local Machine" "Freezes the local machine." "" "" }
|
||||
{ThawLocalMachine 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Machine "thaw_local_machine" "Thaw Local Machine" "Thaws the local machine." "" "" }
|
||||
{FreezeEntity 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Unlocked "freeze_entity" "Freeze Entity" "Freezes an entity." "" "" }
|
||||
{ThawEntity 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Locked "thaw_entity" "Thaw Entity" "Thaws an entity." "" "" }
|
||||
|
||||
//- rjf: entity decoration
|
||||
{SetEntityColor 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Null "set_entity_color" "Set Entity Color" "Sets the passed entity's color." "" "" }
|
||||
{SetEntityName 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Null "set_entity_name" "Set Entity Name" "Sets the passed entity's name." "" "" }
|
||||
|
||||
//- rjf: attaching
|
||||
{Attach 1 1 0 0 "query:unattached_processes" PID null Nil Null 0 0 0 0 0 1 1 Null "attach" "Attach" "Attaches to a process that is already running on the local machine." "" "" }
|
||||
}
|
||||
|
||||
@enum D_CmdKind:
|
||||
{
|
||||
Null,
|
||||
@expand(D_CmdTable, a) `$(a.name)`,
|
||||
COUNT,
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Developer Toggles
|
||||
|
||||
@table(name)
|
||||
D_DevToggleTable:
|
||||
{
|
||||
{always_refresh}
|
||||
{simulate_lag}
|
||||
{draw_ui_text_pos}
|
||||
{draw_ui_focus_debug}
|
||||
{draw_ui_box_heatmap}
|
||||
{eval_compiler_tooltips}
|
||||
{eval_watch_key_tooltips}
|
||||
{cmd_context_tooltips}
|
||||
{updating_indicator}
|
||||
}
|
||||
|
||||
@gen
|
||||
{
|
||||
@expand(D_DevToggleTable a) `global B32 DEV_$(a.name) = 0;`
|
||||
}
|
||||
|
||||
@gen
|
||||
{
|
||||
`struct {B32 *value_ptr; String8 name;} DEV_toggle_table[] =`;
|
||||
`{`;
|
||||
@expand(D_DevToggleTable a) `{&DEV_$(a.name), str8_lit_comp("$(a.name)")},`
|
||||
`};`;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,438 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DBG_ENGINE_CORE_H
|
||||
#define DBG_ENGINE_CORE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Tick Input Types
|
||||
|
||||
typedef struct D_Target D_Target;
|
||||
struct D_Target
|
||||
{
|
||||
String8 exe;
|
||||
String8 args;
|
||||
String8 working_directory;
|
||||
String8 custom_entry_point_name;
|
||||
String8 stdout_path;
|
||||
String8 stderr_path;
|
||||
String8 stdin_path;
|
||||
B32 debug_subprocesses;
|
||||
String8List env;
|
||||
};
|
||||
|
||||
typedef struct D_TargetArray D_TargetArray;
|
||||
struct D_TargetArray
|
||||
{
|
||||
D_Target *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef U32 D_BreakpointFlags;
|
||||
enum
|
||||
{
|
||||
D_BreakpointFlag_BreakOnWrite = (1<<0),
|
||||
D_BreakpointFlag_BreakOnRead = (1<<1),
|
||||
D_BreakpointFlag_BreakOnExecute = (1<<2),
|
||||
};
|
||||
|
||||
typedef struct D_Breakpoint D_Breakpoint;
|
||||
struct D_Breakpoint
|
||||
{
|
||||
D_BreakpointFlags flags;
|
||||
U64 id;
|
||||
String8 file_path;
|
||||
TxtPt pt;
|
||||
String8 vaddr_expr;
|
||||
String8 condition;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
typedef struct D_BreakpointArray D_BreakpointArray;
|
||||
struct D_BreakpointArray
|
||||
{
|
||||
D_Breakpoint *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct D_PathMap D_PathMap;
|
||||
struct D_PathMap
|
||||
{
|
||||
String8 src;
|
||||
String8 dst;
|
||||
};
|
||||
|
||||
typedef struct D_PathMapArray D_PathMapArray;
|
||||
struct D_PathMapArray
|
||||
{
|
||||
D_PathMap *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Trap Nets
|
||||
|
||||
typedef struct D_TrapNet D_TrapNet;
|
||||
struct D_TrapNet
|
||||
{
|
||||
CTRL_TrapList traps;
|
||||
B32 good_line_info;
|
||||
B32 good_read;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Tick Output Types
|
||||
|
||||
typedef enum D_EventKind
|
||||
{
|
||||
D_EventKind_Null,
|
||||
D_EventKind_ModuleLoad,
|
||||
D_EventKind_ProcessEnd,
|
||||
D_EventKind_Stop,
|
||||
D_EventKind_COUNT
|
||||
}
|
||||
D_EventKind;
|
||||
|
||||
typedef enum D_EventCause
|
||||
{
|
||||
D_EventCause_Null,
|
||||
D_EventCause_UserBreakpoint,
|
||||
D_EventCause_Halt,
|
||||
D_EventCause_SoftHalt,
|
||||
D_EventCause_COUNT
|
||||
}
|
||||
D_EventCause;
|
||||
|
||||
typedef struct D_Event D_Event;
|
||||
struct D_Event
|
||||
{
|
||||
D_EventKind kind;
|
||||
D_EventCause cause;
|
||||
CTRL_Handle module;
|
||||
CTRL_Handle thread;
|
||||
U64 vaddr;
|
||||
U64 code;
|
||||
U64 id;
|
||||
};
|
||||
|
||||
typedef struct D_EventNode D_EventNode;
|
||||
struct D_EventNode
|
||||
{
|
||||
D_EventNode *next;
|
||||
D_Event v;
|
||||
};
|
||||
|
||||
typedef struct D_EventList D_EventList;
|
||||
struct D_EventList
|
||||
{
|
||||
D_EventNode *first;
|
||||
D_EventNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Line Info Types
|
||||
|
||||
typedef struct D_Line D_Line;
|
||||
struct D_Line
|
||||
{
|
||||
String8 file_path;
|
||||
TxtPt pt;
|
||||
Rng1U64 voff_range;
|
||||
DI_Key dbgi_key;
|
||||
};
|
||||
|
||||
typedef struct D_LineNode D_LineNode;
|
||||
struct D_LineNode
|
||||
{
|
||||
D_LineNode *next;
|
||||
D_Line v;
|
||||
};
|
||||
|
||||
typedef struct D_LineList D_LineList;
|
||||
struct D_LineList
|
||||
{
|
||||
D_LineNode *first;
|
||||
D_LineNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct D_LineListArray D_LineListArray;
|
||||
struct D_LineListArray
|
||||
{
|
||||
D_LineList *v;
|
||||
U64 count;
|
||||
DI_KeyList dbgi_keys;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debug Engine Control Communication Types
|
||||
|
||||
typedef enum D_RunKind
|
||||
{
|
||||
D_RunKind_Run,
|
||||
D_RunKind_SingleStep,
|
||||
D_RunKind_Step,
|
||||
D_RunKind_COUNT
|
||||
}
|
||||
D_RunKind;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Generated Code
|
||||
|
||||
#include "dbg_engine/generated/dbg_engine.meta.h"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Command Types
|
||||
|
||||
typedef struct D_CmdParams D_CmdParams;
|
||||
struct D_CmdParams
|
||||
{
|
||||
CTRL_Handle machine;
|
||||
CTRL_Handle process;
|
||||
CTRL_Handle thread;
|
||||
CTRL_Handle entity;
|
||||
String8 string;
|
||||
String8 file_path;
|
||||
TxtPt cursor;
|
||||
U64 vaddr;
|
||||
B32 prefer_disasm;
|
||||
U32 pid;
|
||||
U32 rgba;
|
||||
D_TargetArray targets;
|
||||
U64 retry_idx;
|
||||
};
|
||||
|
||||
typedef struct D_Cmd D_Cmd;
|
||||
struct D_Cmd
|
||||
{
|
||||
D_CmdKind kind;
|
||||
D_CmdParams params;
|
||||
};
|
||||
|
||||
typedef struct D_CmdNode D_CmdNode;
|
||||
struct D_CmdNode
|
||||
{
|
||||
D_CmdNode *next;
|
||||
D_CmdNode *prev;
|
||||
D_Cmd cmd;
|
||||
};
|
||||
|
||||
typedef struct D_CmdList D_CmdList;
|
||||
struct D_CmdList
|
||||
{
|
||||
D_CmdNode *first;
|
||||
D_CmdNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main State Caches
|
||||
|
||||
//- rjf: per-run tls-base-vaddr cache
|
||||
|
||||
typedef struct D_RunTLSBaseCacheNode D_RunTLSBaseCacheNode;
|
||||
struct D_RunTLSBaseCacheNode
|
||||
{
|
||||
D_RunTLSBaseCacheNode *hash_next;
|
||||
CTRL_Handle process;
|
||||
U64 root_vaddr;
|
||||
U64 rip_vaddr;
|
||||
U64 tls_base_vaddr;
|
||||
};
|
||||
|
||||
typedef struct D_RunTLSBaseCacheSlot D_RunTLSBaseCacheSlot;
|
||||
struct D_RunTLSBaseCacheSlot
|
||||
{
|
||||
D_RunTLSBaseCacheNode *first;
|
||||
D_RunTLSBaseCacheNode *last;
|
||||
};
|
||||
|
||||
typedef struct D_RunTLSBaseCache D_RunTLSBaseCache;
|
||||
struct D_RunTLSBaseCache
|
||||
{
|
||||
Arena *arena;
|
||||
U64 slots_count;
|
||||
D_RunTLSBaseCacheSlot *slots;
|
||||
};
|
||||
|
||||
//- rjf: per-run locals cache
|
||||
|
||||
typedef struct D_RunLocalsCacheNode D_RunLocalsCacheNode;
|
||||
struct D_RunLocalsCacheNode
|
||||
{
|
||||
D_RunLocalsCacheNode *hash_next;
|
||||
DI_Key dbgi_key;
|
||||
U64 voff;
|
||||
E_String2NumMap *locals_map;
|
||||
};
|
||||
|
||||
typedef struct D_RunLocalsCacheSlot D_RunLocalsCacheSlot;
|
||||
struct D_RunLocalsCacheSlot
|
||||
{
|
||||
D_RunLocalsCacheNode *first;
|
||||
D_RunLocalsCacheNode *last;
|
||||
};
|
||||
|
||||
typedef struct D_RunLocalsCache D_RunLocalsCache;
|
||||
struct D_RunLocalsCache
|
||||
{
|
||||
Arena *arena;
|
||||
U64 table_size;
|
||||
D_RunLocalsCacheSlot *table;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main State Types
|
||||
|
||||
typedef struct D_State D_State;
|
||||
struct D_State
|
||||
{
|
||||
// rjf: top-level state
|
||||
Arena *arena;
|
||||
U64 frame_index;
|
||||
|
||||
// rjf: commands
|
||||
Arena *cmds_arena;
|
||||
D_CmdList cmds;
|
||||
|
||||
// rjf: output log key
|
||||
C_Key output_log_key;
|
||||
|
||||
// rjf: per-run caches
|
||||
U64 tls_base_cache_reggen_idx;
|
||||
U64 tls_base_cache_memgen_idx;
|
||||
D_RunTLSBaseCache tls_base_caches[2];
|
||||
U64 tls_base_cache_gen;
|
||||
U64 locals_cache_reggen_idx;
|
||||
D_RunLocalsCache locals_caches[2];
|
||||
U64 locals_cache_gen;
|
||||
U64 member_cache_reggen_idx;
|
||||
D_RunLocalsCache member_caches[2];
|
||||
U64 member_cache_gen;
|
||||
|
||||
// rjf: user -> ctrl driving state
|
||||
Arena *ctrl_last_run_arena;
|
||||
D_RunKind ctrl_last_run_kind;
|
||||
U64 ctrl_last_run_frame_idx;
|
||||
CTRL_Handle ctrl_last_run_thread_handle;
|
||||
CTRL_RunFlags ctrl_last_run_flags;
|
||||
CTRL_TrapList ctrl_last_run_traps;
|
||||
D_BreakpointArray ctrl_last_run_extra_bps;
|
||||
U128 ctrl_last_run_param_state_hash;
|
||||
B32 ctrl_is_running;
|
||||
B32 ctrl_thread_run_state;
|
||||
B32 ctrl_soft_halt_issued;
|
||||
Arena *ctrl_msg_arena;
|
||||
CTRL_MsgList ctrl_msgs;
|
||||
|
||||
// rjf: ctrl -> user reading state
|
||||
CTRL_EntityCtxRWStore *ctrl_entity_store;
|
||||
Arena *ctrl_stop_arena;
|
||||
CTRL_Event ctrl_last_stop_event;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global D_State *d_state = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal U64 d_hash_from_seed_string(U64 seed, String8 string);
|
||||
internal U64 d_hash_from_string(String8 string);
|
||||
internal U64 d_hash_from_seed_string__case_insensitive(U64 seed, String8 string);
|
||||
internal U64 d_hash_from_string__case_insensitive(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Breakpoints
|
||||
|
||||
internal D_BreakpointArray d_breakpoint_array_copy(Arena *arena, D_BreakpointArray *src);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Path Map Application
|
||||
|
||||
internal String8List d_possible_path_overrides_from_maps_path(Arena *arena, D_PathMapArray *path_maps, String8 file_path);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debug Info Extraction Type Pure Functions
|
||||
|
||||
internal D_LineList d_line_list_copy(Arena *arena, D_LineList *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Command Type Functions
|
||||
|
||||
//- rjf: command parameters
|
||||
internal D_CmdParams d_cmd_params_copy(Arena *arena, D_CmdParams *src);
|
||||
|
||||
//- rjf: command lists
|
||||
internal void d_cmd_list_push_new(Arena *arena, D_CmdList *cmds, D_CmdKind kind, D_CmdParams *params);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Stepping "Trap Net" Builders
|
||||
|
||||
internal D_TrapNet d_trap_net_from_thread__step_over_inst(Arena *arena, CTRL_Entity *thread);
|
||||
internal D_TrapNet d_trap_net_from_thread__step_over_line(Arena *arena, CTRL_Entity *thread);
|
||||
internal D_TrapNet d_trap_net_from_thread__step_into_line(Arena *arena, CTRL_Entity *thread);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debug Info Lookups
|
||||
|
||||
//- rjf: voff -> line info
|
||||
internal D_LineList d_lines_from_dbgi_key_voff(Arena *arena, DI_Key dbgi_key, U64 voff);
|
||||
|
||||
//- rjf: file:line -> line info
|
||||
// TODO(rjf): this depends on file path maps, needs to move
|
||||
// TODO(rjf): need to clean this up & dedup
|
||||
internal D_LineListArray d_lines_array_from_dbgi_key_file_path_line_range(Arena *arena, DI_Key dbgi_key, String8 file_path, Rng1S64 line_num_range);
|
||||
internal D_LineListArray d_lines_array_from_file_path_line_range(Arena *arena, String8 file_path, Rng1S64 line_num_range);
|
||||
internal D_LineList d_lines_from_dbgi_key_file_path_line_num(Arena *arena, DI_Key dbgi_key, String8 file_path, S64 line_num);
|
||||
internal D_LineList d_lines_from_file_path_line_num(Arena *arena, String8 file_path, S64 line_num);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Process/Thread/Module Info Lookups
|
||||
|
||||
internal U64 d_tls_base_vaddr_from_process_root_rip(CTRL_Entity *process, U64 root_vaddr, U64 rip_vaddr);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Target Controls
|
||||
|
||||
//- rjf: stopped info from the control thread
|
||||
internal CTRL_Event d_ctrl_last_stop_event(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main State Accessors/Mutators
|
||||
|
||||
//- rjf: frame data
|
||||
internal U64 d_frame_index(void);
|
||||
|
||||
//- rjf: control state
|
||||
internal D_RunKind d_ctrl_last_run_kind(void);
|
||||
internal U64 d_ctrl_last_run_frame_idx(void);
|
||||
internal B32 d_ctrl_targets_running(void);
|
||||
|
||||
//- rjf: active entity based queries
|
||||
internal DI_KeyList d_push_active_dbgi_key_list(Arena *arena);
|
||||
|
||||
//- rjf: per-run caches
|
||||
internal U64 d_query_cached_rip_from_thread(CTRL_Entity *thread);
|
||||
internal U64 d_query_cached_rip_from_thread_unwind(CTRL_Entity *thread, U64 unwind_count);
|
||||
internal U64 d_query_cached_tls_base_vaddr_from_process_root_rip(CTRL_Entity *process, U64 root_vaddr, U64 rip_vaddr);
|
||||
internal E_String2NumMap *d_query_cached_locals_map_from_dbgi_key_voff(DI_Key dbgi_key, U64 voff);
|
||||
internal E_String2NumMap *d_query_cached_member_map_from_dbgi_key_voff(DI_Key dbgi_key, U64 voff);
|
||||
|
||||
//- rjf: top-level command dispatch
|
||||
internal void d_push_cmd(D_CmdKind kind, D_CmdParams *params);
|
||||
#define d_cmd(kind, ...) d_push_cmd((kind), &(D_CmdParams){.thread = {0}, __VA_ARGS__})
|
||||
|
||||
//- rjf: command iteration
|
||||
internal B32 d_next_cmd(D_Cmd **cmd);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Top-Level Calls
|
||||
|
||||
internal void d_init(void);
|
||||
internal D_EventList d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_PathMapArray *path_maps, U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64]);
|
||||
|
||||
#endif // DBG_ENGINE_CORE_H
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#include "dbg_engine_core.c"
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DBG_ENGINE_INC_H
|
||||
#define DBG_ENGINE_INC_H
|
||||
|
||||
#include "dbg_engine_core.h"
|
||||
|
||||
#endif // DBG_ENGINE_INC_H
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
#ifndef DBG_ENGINE_META_H
|
||||
#define DBG_ENGINE_META_H
|
||||
|
||||
typedef enum D_CmdKind
|
||||
{
|
||||
D_CmdKind_Null,
|
||||
D_CmdKind_LaunchAndRun,
|
||||
D_CmdKind_LaunchAndStepInto,
|
||||
D_CmdKind_Kill,
|
||||
D_CmdKind_KillAll,
|
||||
D_CmdKind_Detach,
|
||||
D_CmdKind_Continue,
|
||||
D_CmdKind_StepIntoInst,
|
||||
D_CmdKind_StepOverInst,
|
||||
D_CmdKind_StepIntoLine,
|
||||
D_CmdKind_StepOverLine,
|
||||
D_CmdKind_StepOut,
|
||||
D_CmdKind_Halt,
|
||||
D_CmdKind_SoftHaltRefresh,
|
||||
D_CmdKind_SetThreadIP,
|
||||
D_CmdKind_RunToLine,
|
||||
D_CmdKind_Run,
|
||||
D_CmdKind_Restart,
|
||||
D_CmdKind_StepInto,
|
||||
D_CmdKind_StepOver,
|
||||
D_CmdKind_FreezeThread,
|
||||
D_CmdKind_ThawThread,
|
||||
D_CmdKind_FreezeProcess,
|
||||
D_CmdKind_ThawProcess,
|
||||
D_CmdKind_FreezeMachine,
|
||||
D_CmdKind_ThawMachine,
|
||||
D_CmdKind_FreezeLocalMachine,
|
||||
D_CmdKind_ThawLocalMachine,
|
||||
D_CmdKind_FreezeEntity,
|
||||
D_CmdKind_ThawEntity,
|
||||
D_CmdKind_SetEntityColor,
|
||||
D_CmdKind_SetEntityName,
|
||||
D_CmdKind_Attach,
|
||||
D_CmdKind_COUNT,
|
||||
} D_CmdKind;
|
||||
|
||||
global B32 DEV_always_refresh = 0;
|
||||
global B32 DEV_simulate_lag = 0;
|
||||
global B32 DEV_draw_ui_text_pos = 0;
|
||||
global B32 DEV_draw_ui_focus_debug = 0;
|
||||
global B32 DEV_draw_ui_box_heatmap = 0;
|
||||
global B32 DEV_eval_compiler_tooltips = 0;
|
||||
global B32 DEV_eval_watch_key_tooltips = 0;
|
||||
global B32 DEV_cmd_context_tooltips = 0;
|
||||
global B32 DEV_updating_indicator = 0;
|
||||
struct {B32 *value_ptr; String8 name;} DEV_toggle_table[] =
|
||||
{
|
||||
{&DEV_always_refresh, str8_lit_comp("always_refresh")},
|
||||
{&DEV_simulate_lag, str8_lit_comp("simulate_lag")},
|
||||
{&DEV_draw_ui_text_pos, str8_lit_comp("draw_ui_text_pos")},
|
||||
{&DEV_draw_ui_focus_debug, str8_lit_comp("draw_ui_focus_debug")},
|
||||
{&DEV_draw_ui_box_heatmap, str8_lit_comp("draw_ui_box_heatmap")},
|
||||
{&DEV_eval_compiler_tooltips, str8_lit_comp("eval_compiler_tooltips")},
|
||||
{&DEV_eval_watch_key_tooltips, str8_lit_comp("eval_watch_key_tooltips")},
|
||||
{&DEV_cmd_context_tooltips, str8_lit_comp("cmd_context_tooltips")},
|
||||
{&DEV_updating_indicator, str8_lit_comp("updating_indicator")},
|
||||
};
|
||||
#endif // DBG_ENGINE_META_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,365 +0,0 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DBG_INFO_H
|
||||
#define DBG_INFO_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Unique Debug Info Key
|
||||
|
||||
typedef struct DI_Key DI_Key;
|
||||
struct DI_Key
|
||||
{
|
||||
U64 u64[2];
|
||||
};
|
||||
|
||||
typedef struct DI_KeyNode DI_KeyNode;
|
||||
struct DI_KeyNode
|
||||
{
|
||||
DI_KeyNode *next;
|
||||
DI_Key v;
|
||||
};
|
||||
|
||||
typedef struct DI_KeyList DI_KeyList;
|
||||
struct DI_KeyList
|
||||
{
|
||||
DI_KeyNode *first;
|
||||
DI_KeyNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DI_KeyArray DI_KeyArray;
|
||||
struct DI_KeyArray
|
||||
{
|
||||
DI_Key *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debug Info Path / Timestamp => Key Cache Types
|
||||
|
||||
typedef struct DI_KeyPathNode DI_KeyPathNode;
|
||||
struct DI_KeyPathNode
|
||||
{
|
||||
DI_KeyPathNode *next;
|
||||
DI_KeyPathNode *prev;
|
||||
String8 path;
|
||||
U64 min_timestamp;
|
||||
DI_Key key;
|
||||
};
|
||||
|
||||
typedef struct DI_KeySlot DI_KeySlot;
|
||||
struct DI_KeySlot
|
||||
{
|
||||
DI_KeyPathNode *first;
|
||||
DI_KeyPathNode *last;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debug Info Cache Types
|
||||
|
||||
typedef struct DI_Node DI_Node;
|
||||
struct DI_Node
|
||||
{
|
||||
// rjf: links
|
||||
DI_Node *next;
|
||||
DI_Node *prev;
|
||||
|
||||
// rjf: key
|
||||
DI_Key key;
|
||||
|
||||
// rjf: value
|
||||
OS_Handle file;
|
||||
OS_Handle file_map;
|
||||
void *file_base;
|
||||
FileProperties file_props;
|
||||
Arena *arena;
|
||||
RDI_Parsed rdi;
|
||||
|
||||
// rjf: metadata
|
||||
AccessPt access_pt;
|
||||
U64 refcount;
|
||||
U64 batch_request_counts[2];
|
||||
U64 working_count;
|
||||
U64 completion_count;
|
||||
};
|
||||
|
||||
typedef struct DI_Slot DI_Slot;
|
||||
struct DI_Slot
|
||||
{
|
||||
DI_Node *first;
|
||||
DI_Node *last;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Requests
|
||||
|
||||
typedef struct DI_Request DI_Request;
|
||||
struct DI_Request
|
||||
{
|
||||
DI_Key key;
|
||||
};
|
||||
|
||||
typedef struct DI_RequestNode DI_RequestNode;
|
||||
struct DI_RequestNode
|
||||
{
|
||||
DI_RequestNode *next;
|
||||
DI_Request v;
|
||||
};
|
||||
|
||||
typedef struct DI_RequestBatch DI_RequestBatch;
|
||||
struct DI_RequestBatch
|
||||
{
|
||||
Mutex mutex;
|
||||
Arena *arena;
|
||||
DI_RequestNode *first;
|
||||
DI_RequestNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Load Tasks
|
||||
|
||||
typedef enum DI_LoadTaskStatus
|
||||
{
|
||||
DI_LoadTaskStatus_Null,
|
||||
DI_LoadTaskStatus_Active,
|
||||
DI_LoadTaskStatus_Done,
|
||||
}
|
||||
DI_LoadTaskStatus;
|
||||
|
||||
typedef struct DI_LoadTask DI_LoadTask;
|
||||
struct DI_LoadTask
|
||||
{
|
||||
DI_LoadTask *next;
|
||||
DI_LoadTask *prev;
|
||||
|
||||
DI_Key key;
|
||||
DI_LoadTaskStatus status;
|
||||
|
||||
B32 og_analyzed;
|
||||
B32 og_is_rdi;
|
||||
U64 og_size;
|
||||
|
||||
B32 rdi_analyzed;
|
||||
B32 rdi_is_stale;
|
||||
|
||||
U64 thread_count;
|
||||
OS_Handle process;
|
||||
};
|
||||
|
||||
typedef struct DI_LoadCompletion DI_LoadCompletion;
|
||||
struct DI_LoadCompletion
|
||||
{
|
||||
DI_LoadCompletion *next;
|
||||
U64 code;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Search Types
|
||||
|
||||
typedef struct DI_SearchItem DI_SearchItem;
|
||||
struct DI_SearchItem
|
||||
{
|
||||
U64 idx;
|
||||
DI_Key key;
|
||||
U64 missed_size;
|
||||
FuzzyMatchRangeList match_ranges;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchItemChunk DI_SearchItemChunk;
|
||||
struct DI_SearchItemChunk
|
||||
{
|
||||
DI_SearchItemChunk *next;
|
||||
U64 base_idx;
|
||||
DI_SearchItem *v;
|
||||
U64 count;
|
||||
U64 cap;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchItemChunkList DI_SearchItemChunkList;
|
||||
struct DI_SearchItemChunkList
|
||||
{
|
||||
DI_SearchItemChunk *first;
|
||||
DI_SearchItemChunk *last;
|
||||
U64 chunk_count;
|
||||
U64 total_count;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchItemArray DI_SearchItemArray;
|
||||
struct DI_SearchItemArray
|
||||
{
|
||||
DI_SearchItem *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Match Types
|
||||
|
||||
typedef struct DI_Match DI_Match;
|
||||
struct DI_Match
|
||||
{
|
||||
DI_Key key;
|
||||
RDI_SectionKind section_kind;
|
||||
U32 idx;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Events
|
||||
|
||||
typedef enum DI_EventKind
|
||||
{
|
||||
DI_EventKind_Null,
|
||||
DI_EventKind_ConversionStarted,
|
||||
DI_EventKind_ConversionEnded,
|
||||
DI_EventKind_COUNT
|
||||
}
|
||||
DI_EventKind;
|
||||
|
||||
typedef struct DI_Event DI_Event;
|
||||
struct DI_Event
|
||||
{
|
||||
DI_EventKind kind;
|
||||
String8 string;
|
||||
};
|
||||
|
||||
typedef struct DI_EventNode DI_EventNode;
|
||||
struct DI_EventNode
|
||||
{
|
||||
DI_EventNode *next;
|
||||
DI_Event v;
|
||||
};
|
||||
|
||||
typedef struct DI_EventList DI_EventList;
|
||||
struct DI_EventList
|
||||
{
|
||||
DI_EventNode *first;
|
||||
DI_EventNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Shared State
|
||||
|
||||
typedef struct DI_Shared DI_Shared;
|
||||
struct DI_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
U64 load_gen;
|
||||
U64 load_count;
|
||||
|
||||
// rjf: key -> path cache
|
||||
U64 key2path_slots_count;
|
||||
DI_KeySlot *key2path_slots;
|
||||
StripeArray key2path_stripes;
|
||||
|
||||
// rjf: path -> key cache
|
||||
U64 path2key_slots_count;
|
||||
DI_KeySlot *path2key_slots;
|
||||
StripeArray path2key_stripes;
|
||||
|
||||
// rjf: debug info cache
|
||||
U64 slots_count;
|
||||
DI_Slot *slots;
|
||||
StripeArray stripes;
|
||||
|
||||
// rjf: requests
|
||||
DI_RequestBatch req_batches[2]; // [0] -> high priority, [1] -> low priority
|
||||
|
||||
// rjf: conversion tasks
|
||||
DI_LoadTask *first_load_task[2];
|
||||
DI_LoadTask *last_load_task[2];
|
||||
DI_LoadTask *free_load_task;
|
||||
U64 conversion_process_count;
|
||||
U64 conversion_thread_count;
|
||||
|
||||
// rjf: conversion completion receiving thread
|
||||
U64 conversion_completion_code;
|
||||
String8 conversion_completion_lock_semaphore_name;
|
||||
String8 conversion_completion_signal_semaphore_name;
|
||||
String8 conversion_completion_shared_memory_name;
|
||||
Semaphore conversion_completion_lock_semaphore;
|
||||
Semaphore conversion_completion_signal_semaphore;
|
||||
OS_Handle conversion_completion_shared_memory;
|
||||
U64 *conversion_completion_shared_memory_base;
|
||||
Thread conversion_completion_signal_receiver_thread;
|
||||
|
||||
// rjf: completion batch
|
||||
Mutex completion_mutex;
|
||||
Arena *completion_arena;
|
||||
DI_LoadCompletion *first_completion;
|
||||
DI_LoadCompletion *last_completion;
|
||||
|
||||
// rjf: events
|
||||
Mutex event_mutex;
|
||||
Arena *event_arena;
|
||||
DI_EventList events;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global DI_Shared *di_shared = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Helpers
|
||||
|
||||
internal DI_Key di_key_zero(void);
|
||||
internal B32 di_key_match(DI_Key a, DI_Key b);
|
||||
internal void di_key_list_push(Arena *arena, DI_KeyList *list, DI_Key key);
|
||||
internal DI_KeyArray di_key_array_from_list(Arena *arena, DI_KeyList *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void di_init(CmdLine *cmdline);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Path * Timestamp Cache Submission & Lookup
|
||||
|
||||
internal DI_Key di_key_from_path_timestamp(String8 path, U64 min_timestamp);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debug Info Opening / Closing
|
||||
|
||||
internal void di_open(DI_Key key);
|
||||
internal void di_close(DI_Key key, B32 force_closed);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debug Info Lookups
|
||||
|
||||
internal U64 di_load_gen(void);
|
||||
internal U64 di_load_count(void);
|
||||
internal DI_KeyArray di_push_all_loaded_keys(Arena *arena);
|
||||
internal RDI_Parsed *di_rdi_from_key(Access *access, DI_Key key, B32 high_priority, U64 endt_us);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Events
|
||||
|
||||
internal DI_EventList di_get_events(Arena *arena);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Tick
|
||||
|
||||
internal void di_async_tick(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Conversion Completion Signal Receiver Thread
|
||||
|
||||
internal void di_signal_completion(void);
|
||||
internal void di_conversion_completion_signal_receiver_thread_entry_point(void *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Search Artifact Cache Hooks / Lookups
|
||||
|
||||
internal AC_Artifact di_search_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out);
|
||||
internal void di_search_artifact_destroy(AC_Artifact artifact);
|
||||
internal DI_SearchItemArray di_search_item_array_from_target_query(Access *access, RDI_SectionKind target, String8 query, U64 endt_us, B32 *stale_out);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Match Artifact Cache Hooks / Lookups
|
||||
|
||||
internal AC_Artifact di_match_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out);
|
||||
internal DI_Match di_match_from_string(String8 string, U64 index, DI_Key preferred_dbgi_key, U64 endt_us);
|
||||
|
||||
#endif // DBG_INFO_H
|
||||
+923
@@ -0,0 +1,923 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal U64
|
||||
di_hash_from_string(String8 string, StringMatchFlags match_flags)
|
||||
{
|
||||
U64 result = 5381;
|
||||
for(U64 i = 0; i < string.size; i += 1)
|
||||
{
|
||||
result = ((result << 5) + result) + ((match_flags & StringMatchFlag_CaseInsensitive) ? char_to_lower(string.str[i]) : string.str[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
di_hash_from_key(DI_Key *k)
|
||||
{
|
||||
U64 hash = di_hash_from_string(k->path, StringMatchFlag_CaseInsensitive);
|
||||
return hash;
|
||||
}
|
||||
|
||||
internal B32
|
||||
di_key_match(DI_Key *a, DI_Key *b)
|
||||
{
|
||||
return (str8_match(a->path, b->path, StringMatchFlag_CaseInsensitive) && a->min_timestamp == b->min_timestamp);
|
||||
}
|
||||
|
||||
internal DI_Key
|
||||
di_key_copy(Arena *arena, DI_Key *src)
|
||||
{
|
||||
DI_Key dst = {0};
|
||||
MemoryCopyStruct(&dst, src);
|
||||
dst.path = push_str8_copy(arena, src->path);
|
||||
return dst;
|
||||
}
|
||||
|
||||
internal DI_Key
|
||||
di_normalized_key_from_key(Arena *arena, DI_Key *src)
|
||||
{
|
||||
DI_Key dst = {path_normalized_from_string(arena, src->path), src->min_timestamp};
|
||||
return dst;
|
||||
}
|
||||
|
||||
internal void
|
||||
di_key_list_push(Arena *arena, DI_KeyList *list, DI_Key *key)
|
||||
{
|
||||
DI_KeyNode *n = push_array(arena, DI_KeyNode, 1);
|
||||
MemoryCopyStruct(&n->v, key);
|
||||
SLLQueuePush(list->first, list->last, n);
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
internal DI_KeyArray
|
||||
di_key_array_from_list(Arena *arena, DI_KeyList *list)
|
||||
{
|
||||
DI_KeyArray array = {0};
|
||||
array.count = list->count;
|
||||
array.v = push_array_no_zero(arena, DI_Key, array.count);
|
||||
U64 idx = 0;
|
||||
for(DI_KeyNode *n = list->first; n != 0; n = n->next, idx += 1)
|
||||
{
|
||||
MemoryCopyStruct(&array.v[idx], &n->v);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void
|
||||
di_init(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
di_shared = push_array(arena, DI_Shared, 1);
|
||||
di_shared->arena = arena;
|
||||
di_shared->slots_count = 1024;
|
||||
di_shared->slots = push_array(arena, DI_Slot, di_shared->slots_count);
|
||||
di_shared->stripes_count = Min(di_shared->slots_count, os_logical_core_count());
|
||||
di_shared->stripes = push_array(arena, DI_Stripe, di_shared->stripes_count);
|
||||
for(U64 idx = 0; idx < di_shared->stripes_count; idx += 1)
|
||||
{
|
||||
di_shared->stripes[idx].arena = arena_alloc();
|
||||
di_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
|
||||
di_shared->stripes[idx].cv = os_condition_variable_alloc();
|
||||
}
|
||||
di_shared->u2p_ring_mutex = os_mutex_alloc();
|
||||
di_shared->u2p_ring_cv = os_condition_variable_alloc();
|
||||
di_shared->u2p_ring_size = KB(64);
|
||||
di_shared->u2p_ring_base = push_array_no_zero(arena, U8, di_shared->u2p_ring_size);
|
||||
di_shared->p2u_ring_mutex = os_mutex_alloc();
|
||||
di_shared->p2u_ring_cv = os_condition_variable_alloc();
|
||||
di_shared->p2u_ring_size = KB(64);
|
||||
di_shared->p2u_ring_base = push_array_no_zero(arena, U8, di_shared->p2u_ring_size);
|
||||
di_shared->parse_thread_count = Max(2, os_logical_core_count()/2);
|
||||
di_shared->parse_threads = push_array(arena, OS_Handle, di_shared->parse_thread_count);
|
||||
for(U64 idx = 0; idx < di_shared->parse_thread_count; idx += 1)
|
||||
{
|
||||
di_shared->parse_threads[idx] = os_launch_thread(di_parse_thread__entry_point, (void *)idx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scope Functions
|
||||
|
||||
internal DI_Scope *
|
||||
di_scope_open(void)
|
||||
{
|
||||
if(di_tctx == 0)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
di_tctx = push_array(arena, DI_TCTX, 1);
|
||||
di_tctx->arena = arena;
|
||||
}
|
||||
DI_Scope *scope = di_tctx->free_scope;
|
||||
if(scope != 0)
|
||||
{
|
||||
SLLStackPop(di_tctx->free_scope);
|
||||
}
|
||||
else
|
||||
{
|
||||
scope = push_array_no_zero(di_tctx->arena, DI_Scope, 1);
|
||||
}
|
||||
MemoryZeroStruct(scope);
|
||||
return scope;
|
||||
}
|
||||
|
||||
internal void
|
||||
di_scope_close(DI_Scope *scope)
|
||||
{
|
||||
for(DI_Touch *t = scope->first_touch, *next = 0; t != 0; t = next)
|
||||
{
|
||||
next = t->next;
|
||||
SLLStackPush(di_tctx->free_touch, t);
|
||||
if(t->node != 0)
|
||||
{
|
||||
ins_atomic_u64_dec_eval(&t->node->touch_count);
|
||||
}
|
||||
}
|
||||
SLLStackPush(di_tctx->free_scope, scope);
|
||||
}
|
||||
|
||||
internal void
|
||||
di_scope_touch_node__stripe_mutex_r_guarded(DI_Scope *scope, DI_Node *node)
|
||||
{
|
||||
if(node != 0)
|
||||
{
|
||||
ins_atomic_u64_inc_eval(&node->touch_count);
|
||||
}
|
||||
DI_Touch *touch = di_tctx->free_touch;
|
||||
if(touch != 0)
|
||||
{
|
||||
SLLStackPop(di_tctx->free_touch);
|
||||
}
|
||||
else
|
||||
{
|
||||
touch = push_array_no_zero(di_tctx->arena, DI_Touch, 1);
|
||||
}
|
||||
MemoryZeroStruct(touch);
|
||||
SLLQueuePush(scope->first_touch, scope->last_touch, touch);
|
||||
touch->node = node;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Per-Slot Functions
|
||||
|
||||
internal DI_Node *
|
||||
di_node_from_key_slot__stripe_mutex_r_guarded(DI_Slot *slot, DI_Key *key)
|
||||
{
|
||||
DI_Node *node = 0;
|
||||
StringMatchFlags match_flags = path_match_flags_from_os(operating_system_from_context());
|
||||
U64 most_recent_timestamp = max_U64;
|
||||
for(DI_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(str8_match(n->key.path, key->path, match_flags) &&
|
||||
key->min_timestamp <= n->key.min_timestamp &&
|
||||
(n->key.min_timestamp - key->min_timestamp) <= most_recent_timestamp)
|
||||
{
|
||||
node = n;
|
||||
most_recent_timestamp = (n->key.min_timestamp - key->min_timestamp);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Per-Stripe Functions
|
||||
|
||||
internal U64
|
||||
di_string_bucket_idx_from_string_size(U64 size)
|
||||
{
|
||||
U64 size_rounded = u64_up_to_pow2(size+1);
|
||||
size_rounded = ClampBot((1<<4), size_rounded);
|
||||
U64 bucket_idx = 0;
|
||||
switch(size_rounded)
|
||||
{
|
||||
case 1<<4: {bucket_idx = 0;}break;
|
||||
case 1<<5: {bucket_idx = 1;}break;
|
||||
case 1<<6: {bucket_idx = 2;}break;
|
||||
case 1<<7: {bucket_idx = 3;}break;
|
||||
case 1<<8: {bucket_idx = 4;}break;
|
||||
case 1<<9: {bucket_idx = 5;}break;
|
||||
case 1<<10:{bucket_idx = 6;}break;
|
||||
default:{bucket_idx = ArrayCount(((DI_Stripe *)0)->free_string_chunks)-1;}break;
|
||||
}
|
||||
return bucket_idx;
|
||||
}
|
||||
|
||||
internal String8
|
||||
di_string_alloc__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string)
|
||||
{
|
||||
if(string.size == 0) {return str8_zero();}
|
||||
U64 bucket_idx = di_string_bucket_idx_from_string_size(string.size);
|
||||
DI_StringChunkNode *node = stripe->free_string_chunks[bucket_idx];
|
||||
|
||||
// rjf: pull from bucket free list
|
||||
if(node != 0)
|
||||
{
|
||||
if(bucket_idx == ArrayCount(stripe->free_string_chunks)-1)
|
||||
{
|
||||
node = 0;
|
||||
DI_StringChunkNode *prev = 0;
|
||||
for(DI_StringChunkNode *n = stripe->free_string_chunks[bucket_idx];
|
||||
n != 0;
|
||||
prev = n, n = n->next)
|
||||
{
|
||||
if(n->size >= string.size+1)
|
||||
{
|
||||
if(prev == 0)
|
||||
{
|
||||
stripe->free_string_chunks[bucket_idx] = n->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev->next = n->next;
|
||||
}
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SLLStackPop(stripe->free_string_chunks[bucket_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: no found node -> allocate new
|
||||
if(node == 0)
|
||||
{
|
||||
U64 chunk_size = 0;
|
||||
if(bucket_idx < ArrayCount(stripe->free_string_chunks)-1)
|
||||
{
|
||||
chunk_size = 1<<(bucket_idx+4);
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = u64_up_to_pow2(string.size);
|
||||
}
|
||||
U8 *chunk_memory = push_array(stripe->arena, U8, chunk_size);
|
||||
node = (DI_StringChunkNode *)chunk_memory;
|
||||
}
|
||||
|
||||
// rjf: fill string & return
|
||||
String8 allocated_string = str8((U8 *)node, string.size);
|
||||
MemoryCopy((U8 *)node, string.str, string.size);
|
||||
return allocated_string;
|
||||
}
|
||||
|
||||
internal void
|
||||
di_string_release__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string)
|
||||
{
|
||||
if(string.size == 0) {return;}
|
||||
U64 bucket_idx = di_string_bucket_idx_from_string_size(string.size);
|
||||
DI_StringChunkNode *node = (DI_StringChunkNode *)string.str;
|
||||
node->size = u64_up_to_pow2(string.size);
|
||||
SLLStackPush(stripe->free_string_chunks[bucket_idx], node);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Key Opening/Closing
|
||||
|
||||
internal void
|
||||
di_open(DI_Key *key)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
if(key->path.size != 0)
|
||||
{
|
||||
DI_Key key_normalized = di_normalized_key_from_key(scratch.arena, key);
|
||||
U64 hash = di_hash_from_key(&key_normalized);
|
||||
U64 slot_idx = hash%di_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%di_shared->stripes_count;
|
||||
DI_Slot *slot = &di_shared->slots[slot_idx];
|
||||
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
|
||||
log_infof("opening debug info: %S [0x%I64x]\n", key_normalized.path, key_normalized.min_timestamp);
|
||||
OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
//- rjf: find existing node
|
||||
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key_normalized);
|
||||
|
||||
//- rjf: allocate node if none exists; insert into slot
|
||||
if(node == 0)
|
||||
{
|
||||
U64 current_timestamp = os_properties_from_file_path(key_normalized.path).modified;
|
||||
if(current_timestamp == 0)
|
||||
{
|
||||
current_timestamp = key_normalized.min_timestamp;
|
||||
}
|
||||
node = stripe->free_node;
|
||||
if(node != 0)
|
||||
{
|
||||
SLLStackPop(stripe->free_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = push_array_no_zero(stripe->arena, DI_Node, 1);
|
||||
}
|
||||
MemoryZeroStruct(node);
|
||||
DLLPushBack(slot->first, slot->last, node);
|
||||
String8 path_stored = di_string_alloc__stripe_mutex_w_guarded(stripe, key_normalized.path);
|
||||
node->key.path = path_stored;
|
||||
node->key.min_timestamp = current_timestamp;
|
||||
}
|
||||
|
||||
//- rjf: increment node reference count
|
||||
if(node != 0)
|
||||
{
|
||||
node->ref_count += 1;
|
||||
if(node->ref_count == 1)
|
||||
{
|
||||
di_u2p_enqueue_key(&key_normalized, max_U64);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
di_close(DI_Key *key)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
if(key->path.size != 0)
|
||||
{
|
||||
DI_Key key_normalized = di_normalized_key_from_key(scratch.arena, key);
|
||||
U64 hash = di_hash_from_key(&key_normalized);
|
||||
U64 slot_idx = hash%di_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%di_shared->stripes_count;
|
||||
DI_Slot *slot = &di_shared->slots[slot_idx];
|
||||
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
|
||||
log_infof("closing debug info: %S [0x%I64x]\n", key_normalized.path, key_normalized.min_timestamp);
|
||||
OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
//- rjf: find existing node
|
||||
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key_normalized);
|
||||
|
||||
//- rjf: node exists -> decrement reference count; release
|
||||
if(node != 0)
|
||||
{
|
||||
node->ref_count -= 1;
|
||||
if(node->ref_count == 0) for(;;)
|
||||
{
|
||||
//- rjf: wait for touch count to go to 0
|
||||
if(ins_atomic_u64_eval(&node->touch_count) != 0)
|
||||
{
|
||||
os_rw_mutex_drop_w(stripe->rw_mutex);
|
||||
for(U64 start_t = os_now_microseconds(); os_now_microseconds() <= start_t + 250;);
|
||||
os_rw_mutex_take_w(stripe->rw_mutex);
|
||||
}
|
||||
|
||||
//- rjf: release
|
||||
if(node->ref_count == 0 && ins_atomic_u64_eval(&node->touch_count) == 0)
|
||||
{
|
||||
di_string_release__stripe_mutex_w_guarded(stripe, node->key.path);
|
||||
if(node->file_base != 0)
|
||||
{
|
||||
os_file_map_view_close(node->file_map, node->file_base);
|
||||
}
|
||||
if(!os_handle_match(node->file_map, os_handle_zero()))
|
||||
{
|
||||
os_file_map_close(node->file_map);
|
||||
}
|
||||
if(!os_handle_match(node->file, os_handle_zero()))
|
||||
{
|
||||
os_file_close(node->file);
|
||||
}
|
||||
if(node->arena != 0)
|
||||
{
|
||||
arena_release(node->arena);
|
||||
}
|
||||
DLLRemove(slot->first, slot->last, node);
|
||||
SLLStackPush(stripe->free_node, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal RDI_Parsed *
|
||||
di_rdi_from_key(DI_Scope *scope, DI_Key *key, U64 endt_us)
|
||||
{
|
||||
RDI_Parsed *result = &di_rdi_parsed_nil;
|
||||
if(key->path.size != 0)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
DI_Key key_normalized = di_normalized_key_from_key(scratch.arena, key);
|
||||
U64 hash = di_hash_from_key(&key_normalized);
|
||||
U64 slot_idx = hash%di_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%di_shared->stripes_count;
|
||||
DI_Slot *slot = &di_shared->slots[slot_idx];
|
||||
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
|
||||
OS_MutexScopeR(stripe->rw_mutex) for(;;)
|
||||
{
|
||||
//- rjf: find existing node
|
||||
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key_normalized);
|
||||
|
||||
//- rjf: no node? this path is not opened
|
||||
if(node == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//- rjf: parse done -> touch, grab result
|
||||
if(node != 0 && node->parse_done)
|
||||
{
|
||||
di_scope_touch_node__stripe_mutex_r_guarded(scope, node);
|
||||
result = &node->rdi;
|
||||
break;
|
||||
}
|
||||
|
||||
//- rjf: parse not done, not working, asked a while ago -> ask for parse
|
||||
B32 sent = 0;
|
||||
if(node != 0 && !node->parse_done && !node->is_working && ins_atomic_u64_eval(&node->last_time_requested_us)+1000000<os_now_microseconds())
|
||||
{
|
||||
sent = di_u2p_enqueue_key(&key_normalized, endt_us);
|
||||
if(sent)
|
||||
{
|
||||
ins_atomic_u64_eval_assign(&node->last_time_requested_us, os_now_microseconds());
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: time expired -> break
|
||||
if(os_now_microseconds() >= endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//- rjf: wait on this stripe
|
||||
{
|
||||
os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parse Threads
|
||||
|
||||
internal B32
|
||||
di_u2p_enqueue_key(DI_Key *key, U64 endt_us)
|
||||
{
|
||||
B32 sent = 0;
|
||||
OS_MutexScope(di_shared->u2p_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = di_shared->u2p_ring_write_pos - di_shared->u2p_ring_read_pos;
|
||||
U64 available_size = di_shared->u2p_ring_size - unconsumed_size;
|
||||
if(available_size >= sizeof(key->path.size) + key->path.size + sizeof(key->min_timestamp))
|
||||
{
|
||||
di_shared->u2p_ring_write_pos += ring_write_struct(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_write_pos, &key->path.size);
|
||||
di_shared->u2p_ring_write_pos += ring_write(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_write_pos, key->path.str, key->path.size);
|
||||
di_shared->u2p_ring_write_pos += ring_write_struct(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_write_pos, &key->min_timestamp);
|
||||
di_shared->u2p_ring_write_pos += 7;
|
||||
di_shared->u2p_ring_write_pos -= di_shared->u2p_ring_write_pos%8;
|
||||
sent = 1;
|
||||
break;
|
||||
}
|
||||
if(os_now_microseconds() >= endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(di_shared->u2p_ring_cv, di_shared->u2p_ring_mutex, endt_us);
|
||||
}
|
||||
if(sent)
|
||||
{
|
||||
os_condition_variable_broadcast(di_shared->u2p_ring_cv);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
internal void
|
||||
di_u2p_dequeue_key(Arena *arena, DI_Key *out_key)
|
||||
{
|
||||
OS_MutexScope(di_shared->u2p_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = di_shared->u2p_ring_write_pos - di_shared->u2p_ring_read_pos;
|
||||
if(unconsumed_size >= sizeof(out_key->path.size) + sizeof(out_key->min_timestamp))
|
||||
{
|
||||
di_shared->u2p_ring_read_pos += ring_read_struct(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_read_pos, &out_key->path.size);
|
||||
out_key->path.str = push_array(arena, U8, out_key->path.size);
|
||||
di_shared->u2p_ring_read_pos += ring_read(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_read_pos, out_key->path.str, out_key->path.size);
|
||||
di_shared->u2p_ring_read_pos += ring_read_struct(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_read_pos, &out_key->min_timestamp);
|
||||
di_shared->u2p_ring_read_pos += 7;
|
||||
di_shared->u2p_ring_read_pos -= di_shared->u2p_ring_read_pos%8;
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(di_shared->u2p_ring_cv, di_shared->u2p_ring_mutex, max_U64);
|
||||
}
|
||||
os_condition_variable_broadcast(di_shared->u2p_ring_cv);
|
||||
}
|
||||
|
||||
internal void
|
||||
di_p2u_push_event(DI_Event *event)
|
||||
{
|
||||
OS_MutexScope(di_shared->p2u_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = (di_shared->p2u_ring_write_pos-di_shared->p2u_ring_read_pos);
|
||||
U64 available_size = di_shared->p2u_ring_size-unconsumed_size;
|
||||
U64 needed_size = sizeof(DI_EventKind) + sizeof(U64) + event->string.size;
|
||||
if(available_size >= needed_size)
|
||||
{
|
||||
di_shared->p2u_ring_write_pos += ring_write_struct(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_write_pos, &event->kind);
|
||||
di_shared->p2u_ring_write_pos += ring_write_struct(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_write_pos, &event->string.size);
|
||||
di_shared->p2u_ring_write_pos += ring_write(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_write_pos, event->string.str, event->string.size);
|
||||
di_shared->p2u_ring_write_pos += 7;
|
||||
di_shared->p2u_ring_write_pos -= di_shared->p2u_ring_write_pos%8;
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(di_shared->p2u_ring_cv, di_shared->p2u_ring_mutex, max_U64);
|
||||
}
|
||||
os_condition_variable_broadcast(di_shared->p2u_ring_cv);
|
||||
}
|
||||
|
||||
internal DI_EventList
|
||||
di_p2u_pop_events(Arena *arena, U64 endt_us)
|
||||
{
|
||||
DI_EventList events = {0};
|
||||
OS_MutexScope(di_shared->p2u_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = (di_shared->p2u_ring_write_pos-di_shared->p2u_ring_read_pos);
|
||||
if(unconsumed_size >= sizeof(DI_EventKind) + sizeof(U64))
|
||||
{
|
||||
DI_EventNode *n = push_array(arena, DI_EventNode, 1);
|
||||
SLLQueuePush(events.first, events.last, n);
|
||||
events.count += 1;
|
||||
di_shared->p2u_ring_read_pos += ring_read_struct(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_read_pos, &n->v.kind);
|
||||
di_shared->p2u_ring_read_pos += ring_read_struct(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_read_pos, &n->v.string.size);
|
||||
n->v.string.str = push_array_no_zero(arena, U8, n->v.string.size);
|
||||
di_shared->p2u_ring_read_pos += ring_read(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_read_pos, n->v.string.str, n->v.string.size);
|
||||
di_shared->p2u_ring_read_pos += 7;
|
||||
di_shared->p2u_ring_read_pos -= di_shared->p2u_ring_read_pos%8;
|
||||
}
|
||||
else if(os_now_microseconds() >= endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(di_shared->p2u_ring_cv, di_shared->p2u_ring_mutex, endt_us);
|
||||
}
|
||||
os_condition_variable_broadcast(di_shared->p2u_ring_cv);
|
||||
return events;
|
||||
}
|
||||
|
||||
internal void
|
||||
di_parse_thread__entry_point(void *p)
|
||||
{
|
||||
ThreadNameF("[di] parse #%I64u", (U64)p);
|
||||
for(;;)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: grab next key
|
||||
//
|
||||
DI_Key key = {0};
|
||||
di_u2p_dequeue_key(scratch.arena, &key);
|
||||
String8 og_path = key.path;
|
||||
U64 min_timestamp = key.min_timestamp;
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: unpack key
|
||||
//
|
||||
U64 hash = di_hash_from_string(og_path, StringMatchFlag_CaseInsensitive);
|
||||
U64 slot_idx = hash%di_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%di_shared->stripes_count;
|
||||
DI_Slot *slot = &di_shared->slots[slot_idx];
|
||||
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: take task
|
||||
//
|
||||
B32 got_task = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key);
|
||||
if(node != 0)
|
||||
{
|
||||
got_task = !ins_atomic_u64_eval_cond_assign(&node->is_working, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: got task -> open O.G. file (may or may not be RDI)
|
||||
//
|
||||
B32 og_format_is_known = 0;
|
||||
B32 og_is_pe = 0;
|
||||
B32 og_is_pdb = 0;
|
||||
B32 og_is_elf = 0;
|
||||
B32 og_is_rdi = 0;
|
||||
FileProperties og_props = {0};
|
||||
if(got_task) ProfScope("analyze %.*s", str8_varg(og_path))
|
||||
{
|
||||
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, og_path);
|
||||
OS_Handle file_map = os_file_map_open(OS_AccessFlag_Read, file);
|
||||
FileProperties props = og_props = os_properties_from_file(file);
|
||||
void *base = os_file_map_view_open(file_map, OS_AccessFlag_Read, r1u64(0, props.size));
|
||||
String8 data = str8((U8 *)base, props.size);
|
||||
if(!og_format_is_known)
|
||||
{
|
||||
String8 msf20_magic = str8_lit("Microsoft C/C++ program database 2.00\r\n\x1aJG\0\0");
|
||||
String8 msf70_magic = str8_lit("Microsoft C/C++ MSF 7.00\r\n\032DS\0\0");
|
||||
String8 msfxx_magic = str8_lit("Microsoft C/C++");
|
||||
if((data.size >= msf20_magic.size && str8_match(data, msf20_magic, StringMatchFlag_RightSideSloppy)) ||
|
||||
(data.size >= msf70_magic.size && str8_match(data, msf70_magic, StringMatchFlag_RightSideSloppy)) ||
|
||||
(data.size >= msfxx_magic.size && str8_match(data, msfxx_magic, StringMatchFlag_RightSideSloppy)))
|
||||
{
|
||||
og_format_is_known = 1;
|
||||
og_is_pdb = 1;
|
||||
}
|
||||
}
|
||||
if(!og_format_is_known)
|
||||
{
|
||||
if(data.size >= 8 && *(U64 *)data.str == RDI_MAGIC_CONSTANT)
|
||||
{
|
||||
og_format_is_known = 1;
|
||||
og_is_rdi = 1;
|
||||
}
|
||||
}
|
||||
if(!og_format_is_known)
|
||||
{
|
||||
if(data.size >= 4 &&
|
||||
data.str[0] == 0x7f &&
|
||||
data.str[1] == 'E' &&
|
||||
data.str[2] == 'L' &&
|
||||
data.str[3] == 'F')
|
||||
{
|
||||
og_format_is_known = 1;
|
||||
og_is_elf = 1;
|
||||
}
|
||||
}
|
||||
if(!og_format_is_known)
|
||||
{
|
||||
if(data.size >= 2 && *(U16 *)data.str == 0x5a4d)
|
||||
{
|
||||
og_format_is_known = 1;
|
||||
og_is_pe = 1;
|
||||
}
|
||||
}
|
||||
os_file_map_view_close(file_map, base);
|
||||
os_file_map_close(file_map);
|
||||
os_file_close(file);
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: given O.G. path & analysis, determine RDI path
|
||||
//
|
||||
String8 rdi_path = {0};
|
||||
if(got_task)
|
||||
{
|
||||
if(og_is_rdi)
|
||||
{
|
||||
rdi_path = og_path;
|
||||
}
|
||||
else if(og_format_is_known && og_is_pdb)
|
||||
{
|
||||
rdi_path = push_str8f(scratch.arena, "%S.rdi", str8_chop_last_dot(og_path));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: check if rdi file is up-to-date
|
||||
//
|
||||
B32 rdi_file_is_up_to_date = 0;
|
||||
if(got_task)
|
||||
{
|
||||
if(rdi_path.size != 0) ProfScope("check %.*s is up-to-date", str8_varg(rdi_path))
|
||||
{
|
||||
FileProperties props = os_properties_from_file_path(rdi_path);
|
||||
rdi_file_is_up_to_date = (props.modified > og_props.modified);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: if raddbg file is up to date based on timestamp, check the
|
||||
// encoding generation number & size, to see if we need to regenerate it
|
||||
// regardless
|
||||
//
|
||||
if(got_task && rdi_file_is_up_to_date) ProfScope("check %.*s version matches our's", str8_varg(rdi_path))
|
||||
{
|
||||
OS_Handle file = {0};
|
||||
OS_Handle file_map = {0};
|
||||
FileProperties file_props = {0};
|
||||
void *file_base = 0;
|
||||
file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, rdi_path);
|
||||
file_map = os_file_map_open(OS_AccessFlag_Read, file);
|
||||
file_props = os_properties_from_file(file);
|
||||
file_base = os_file_map_view_open(file_map, OS_AccessFlag_Read, r1u64(0, file_props.size));
|
||||
if(sizeof(RDI_Header) <= file_props.size)
|
||||
{
|
||||
RDI_Header *header = (RDI_Header*)file_base;
|
||||
if(header->encoding_version != RDI_ENCODING_VERSION)
|
||||
{
|
||||
rdi_file_is_up_to_date = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rdi_file_is_up_to_date = 0;
|
||||
}
|
||||
os_file_map_view_close(file_map, file_base);
|
||||
os_file_map_close(file_map);
|
||||
os_file_close(file);
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: heuristically choose compression settings
|
||||
//
|
||||
B32 should_compress = 0;
|
||||
#if 0
|
||||
if(og_dbg_props.size > MB(64))
|
||||
{
|
||||
should_compress = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: rdi file not up-to-date? we need to generate it
|
||||
//
|
||||
if(got_task && !rdi_file_is_up_to_date) ProfScope("generate %.*s", str8_varg(rdi_path))
|
||||
{
|
||||
if(og_is_pdb)
|
||||
{
|
||||
//- rjf: push conversion task begin event
|
||||
{
|
||||
DI_Event event = {DI_EventKind_ConversionStarted};
|
||||
event.string = rdi_path;
|
||||
di_p2u_push_event(&event);
|
||||
}
|
||||
|
||||
//- rjf: kick off process
|
||||
OS_Handle process = {0};
|
||||
{
|
||||
OS_LaunchOptions opts = {0};
|
||||
opts.path = os_string_from_system_path(scratch.arena, OS_SystemPath_Binary);
|
||||
opts.inherit_env = 1;
|
||||
opts.consoleless = 1;
|
||||
str8_list_pushf(scratch.arena, &opts.cmd_line, "raddbg");
|
||||
str8_list_pushf(scratch.arena, &opts.cmd_line, "--convert");
|
||||
str8_list_pushf(scratch.arena, &opts.cmd_line, "--quiet");
|
||||
if(should_compress)
|
||||
{
|
||||
str8_list_pushf(scratch.arena, &opts.cmd_line, "--compress");
|
||||
}
|
||||
//str8_list_pushf(scratch.arena, &opts.cmd_line, "--capture");
|
||||
str8_list_pushf(scratch.arena, &opts.cmd_line, "--pdb:%S", og_path);
|
||||
str8_list_pushf(scratch.arena, &opts.cmd_line, "--out:%S", rdi_path);
|
||||
os_launch_process(&opts, &process);
|
||||
}
|
||||
|
||||
//- rjf: wait for process to complete
|
||||
{
|
||||
U64 start_wait_t = os_now_microseconds();
|
||||
for(;;)
|
||||
{
|
||||
B32 wait_done = os_process_wait(process, os_now_microseconds()+1000);
|
||||
if(wait_done)
|
||||
{
|
||||
rdi_file_is_up_to_date = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: push conversion task end event
|
||||
{
|
||||
DI_Event event = {DI_EventKind_ConversionEnded};
|
||||
event.string = rdi_path;
|
||||
di_p2u_push_event(&event);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE(rjf): we cannot convert from this O.G. debug info format right now.
|
||||
//- rjf: push conversion task failure event
|
||||
{
|
||||
DI_Event event = {DI_EventKind_ConversionFailureUnsupportedFormat};
|
||||
event.string = rdi_path;
|
||||
di_p2u_push_event(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: got task -> open file
|
||||
//
|
||||
OS_Handle file = {0};
|
||||
OS_Handle file_map = {0};
|
||||
FileProperties file_props = {0};
|
||||
void *file_base = 0;
|
||||
if(got_task)
|
||||
{
|
||||
file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, rdi_path);
|
||||
file_map = os_file_map_open(OS_AccessFlag_Read, file);
|
||||
file_props = os_properties_from_file(file);
|
||||
file_base = os_file_map_view_open(file_map, OS_AccessFlag_Read, r1u64(0, file_props.size));
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: do initial parse of rdi
|
||||
//
|
||||
RDI_Parsed rdi_parsed_maybe_compressed = di_rdi_parsed_nil;
|
||||
if(got_task)
|
||||
{
|
||||
RDI_ParseStatus parse_status = rdi_parse((U8 *)file_base, file_props.size, &rdi_parsed_maybe_compressed);
|
||||
(void)parse_status;
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: decompress & re-parse, if necessary
|
||||
//
|
||||
Arena *rdi_parsed_arena = 0;
|
||||
RDI_Parsed rdi_parsed = rdi_parsed_maybe_compressed;
|
||||
if(got_task)
|
||||
{
|
||||
U64 decompressed_size = file_props.size;
|
||||
for(U64 dsec_idx = 0; dsec_idx < rdi_parsed_maybe_compressed.dsec_count; dsec_idx += 1)
|
||||
{
|
||||
decompressed_size += (rdi_parsed_maybe_compressed.dsecs[dsec_idx].unpacked_size - rdi_parsed_maybe_compressed.dsecs[dsec_idx].encoded_size);
|
||||
}
|
||||
if(decompressed_size > file_props.size)
|
||||
{
|
||||
rdi_parsed_arena = arena_alloc();
|
||||
U8 *decompressed_data = push_array_no_zero(rdi_parsed_arena, U8, decompressed_size);
|
||||
|
||||
// rjf: copy header
|
||||
RDI_Header *src_header = (RDI_Header *)file_base;
|
||||
RDI_Header *dst_header = (RDI_Header *)decompressed_data;
|
||||
{
|
||||
MemoryCopy(dst_header, src_header, sizeof(RDI_Header));
|
||||
}
|
||||
|
||||
// rjf: copy & adjust sections for decompressed version
|
||||
if(rdi_parsed_maybe_compressed.dsec_count != 0)
|
||||
{
|
||||
RDI_DataSection *dsec_base = (RDI_DataSection *)(decompressed_data + dst_header->data_section_off);
|
||||
MemoryCopy(dsec_base, (U8 *)file_base + src_header->data_section_off, sizeof(RDI_DataSection) * rdi_parsed_maybe_compressed.dsec_count);
|
||||
U64 off = dst_header->data_section_off + sizeof(RDI_DataSection) * rdi_parsed_maybe_compressed.dsec_count;
|
||||
off += 7;
|
||||
off -= off%8;
|
||||
for(U64 idx = 0; idx < rdi_parsed_maybe_compressed.dsec_count; idx += 1)
|
||||
{
|
||||
dsec_base[idx].encoding = RDI_DataSectionEncoding_Unpacked;
|
||||
dsec_base[idx].off = off;
|
||||
dsec_base[idx].encoded_size = dsec_base[idx].unpacked_size;
|
||||
off += dsec_base[idx].unpacked_size;
|
||||
off += 7;
|
||||
off -= off%8;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: decompress sections into new decompressed file buffer
|
||||
if(rdi_parsed_maybe_compressed.dsec_count != 0)
|
||||
{
|
||||
RDI_DataSection *src_first = rdi_parsed_maybe_compressed.dsecs;
|
||||
RDI_DataSection *dst_first = (RDI_DataSection *)(decompressed_data + dst_header->data_section_off);
|
||||
RDI_DataSection *src_opl = src_first + rdi_parsed_maybe_compressed.dsec_count;
|
||||
RDI_DataSection *dst_opl = dst_first + rdi_parsed_maybe_compressed.dsec_count;
|
||||
for(RDI_DataSection *src = src_first, *dst = dst_first;
|
||||
src < src_opl && dst < dst_opl;
|
||||
src += 1, dst += 1)
|
||||
{
|
||||
rr_lzb_simple_decode((U8*)file_base + src->off, src->encoded_size,
|
||||
decompressed_data + dst->off, dst->unpacked_size);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: re-parse
|
||||
RDI_ParseStatus parse_status = rdi_parse(decompressed_data, decompressed_size, &rdi_parsed);
|
||||
(void)parse_status;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: commit parsed info to cache
|
||||
//
|
||||
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key);
|
||||
if(node != 0)
|
||||
{
|
||||
node->is_working = 0;
|
||||
node->file = file;
|
||||
node->file_map = file_map;
|
||||
node->file_base = file_base;
|
||||
node->file_props = file_props;
|
||||
node->arena = rdi_parsed_arena;
|
||||
node->rdi = rdi_parsed;
|
||||
node->parse_done = 1;
|
||||
}
|
||||
}
|
||||
os_condition_variable_broadcast(stripe->cv);
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DI_H
|
||||
#define DI_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Key Type
|
||||
|
||||
typedef struct DI_Key DI_Key;
|
||||
struct DI_Key
|
||||
{
|
||||
String8 path;
|
||||
U64 min_timestamp;
|
||||
};
|
||||
|
||||
typedef struct DI_KeyNode DI_KeyNode;
|
||||
struct DI_KeyNode
|
||||
{
|
||||
DI_KeyNode *next;
|
||||
DI_Key v;
|
||||
};
|
||||
|
||||
typedef struct DI_KeyList DI_KeyList;
|
||||
struct DI_KeyList
|
||||
{
|
||||
DI_KeyNode *first;
|
||||
DI_KeyNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DI_KeyArray DI_KeyArray;
|
||||
struct DI_KeyArray
|
||||
{
|
||||
DI_Key *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Event Types
|
||||
|
||||
typedef enum DI_EventKind
|
||||
{
|
||||
DI_EventKind_Null,
|
||||
DI_EventKind_ConversionStarted,
|
||||
DI_EventKind_ConversionEnded,
|
||||
DI_EventKind_ConversionFailureUnsupportedFormat,
|
||||
DI_EventKind_COUNT
|
||||
}
|
||||
DI_EventKind;
|
||||
|
||||
typedef struct DI_Event DI_Event;
|
||||
struct DI_Event
|
||||
{
|
||||
DI_EventKind kind;
|
||||
String8 string;
|
||||
};
|
||||
|
||||
typedef struct DI_EventNode DI_EventNode;
|
||||
struct DI_EventNode
|
||||
{
|
||||
DI_EventNode *next;
|
||||
DI_Event v;
|
||||
};
|
||||
|
||||
typedef struct DI_EventList DI_EventList;
|
||||
struct DI_EventList
|
||||
{
|
||||
DI_EventNode *first;
|
||||
DI_EventNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Types
|
||||
|
||||
typedef struct DI_StringChunkNode DI_StringChunkNode;
|
||||
struct DI_StringChunkNode
|
||||
{
|
||||
DI_StringChunkNode *next;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
typedef struct DI_Node DI_Node;
|
||||
struct DI_Node
|
||||
{
|
||||
// rjf: links
|
||||
DI_Node *next;
|
||||
DI_Node *prev;
|
||||
|
||||
// rjf: metadata
|
||||
U64 ref_count;
|
||||
U64 touch_count;
|
||||
U64 is_working;
|
||||
U64 last_time_requested_us;
|
||||
|
||||
// rjf: key
|
||||
DI_Key key;
|
||||
|
||||
// rjf: file handles
|
||||
OS_Handle file;
|
||||
OS_Handle file_map;
|
||||
void *file_base;
|
||||
FileProperties file_props;
|
||||
|
||||
// rjf: parse artifacts
|
||||
Arena *arena;
|
||||
RDI_Parsed rdi;
|
||||
B32 parse_done;
|
||||
};
|
||||
|
||||
typedef struct DI_Slot DI_Slot;
|
||||
struct DI_Slot
|
||||
{
|
||||
DI_Node *first;
|
||||
DI_Node *last;
|
||||
};
|
||||
|
||||
typedef struct DI_Stripe DI_Stripe;
|
||||
struct DI_Stripe
|
||||
{
|
||||
Arena *arena;
|
||||
DI_Node *free_node;
|
||||
DI_StringChunkNode *free_string_chunks[8];
|
||||
OS_Handle rw_mutex;
|
||||
OS_Handle cv;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scoped Access Types
|
||||
|
||||
typedef struct DI_Touch DI_Touch;
|
||||
struct DI_Touch
|
||||
{
|
||||
DI_Touch *next;
|
||||
DI_Node *node;
|
||||
};
|
||||
|
||||
typedef struct DI_Scope DI_Scope;
|
||||
struct DI_Scope
|
||||
{
|
||||
DI_Scope *next;
|
||||
DI_Touch *first_touch;
|
||||
DI_Touch *last_touch;
|
||||
};
|
||||
|
||||
typedef struct DI_TCTX DI_TCTX;
|
||||
struct DI_TCTX
|
||||
{
|
||||
Arena *arena;
|
||||
DI_Scope *free_scope;
|
||||
DI_Touch *free_touch;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Shared State Types
|
||||
|
||||
typedef struct DI_Shared DI_Shared;
|
||||
struct DI_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
|
||||
// rjf: node cache
|
||||
U64 slots_count;
|
||||
DI_Slot *slots;
|
||||
U64 stripes_count;
|
||||
DI_Stripe *stripes;
|
||||
|
||||
// rjf: user -> parse ring
|
||||
OS_Handle u2p_ring_mutex;
|
||||
OS_Handle u2p_ring_cv;
|
||||
U64 u2p_ring_size;
|
||||
U8 *u2p_ring_base;
|
||||
U64 u2p_ring_write_pos;
|
||||
U64 u2p_ring_read_pos;
|
||||
|
||||
// rjf: parse -> user event ring
|
||||
OS_Handle p2u_ring_mutex;
|
||||
OS_Handle p2u_ring_cv;
|
||||
U64 p2u_ring_size;
|
||||
U8 *p2u_ring_base;
|
||||
U64 p2u_ring_write_pos;
|
||||
U64 p2u_ring_read_pos;
|
||||
|
||||
// rjf: threads
|
||||
U64 parse_thread_count;
|
||||
OS_Handle *parse_threads;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global DI_Shared *di_shared = 0;
|
||||
thread_static DI_TCTX *di_tctx = 0;
|
||||
global RDI_Parsed di_rdi_parsed_nil =
|
||||
{
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
{0},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&rdi_top_level_info_nil,
|
||||
&rdi_binary_section_nil, 1,
|
||||
&rdi_file_path_node_nil, 1,
|
||||
&rdi_source_file_nil, 1,
|
||||
&rdi_unit_nil, 1,
|
||||
&rdi_vmap_entry_nil, 1,
|
||||
&rdi_type_node_nil, 1,
|
||||
&rdi_udt_nil, 1,
|
||||
&rdi_member_nil, 1,
|
||||
&rdi_enum_member_nil, 1,
|
||||
&rdi_global_variable_nil, 1,
|
||||
&rdi_vmap_entry_nil, 1,
|
||||
&rdi_thread_variable_nil, 1,
|
||||
&rdi_procedure_nil, 1,
|
||||
&rdi_scope_nil, 1,
|
||||
&rdi_voff_nil, 1,
|
||||
&rdi_vmap_entry_nil, 1,
|
||||
&rdi_local_nil, 1,
|
||||
&rdi_location_block_nil, 1,
|
||||
0, 0,
|
||||
0, 0,
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal U64 di_hash_from_string(String8 string, StringMatchFlags match_flags);
|
||||
internal U64 di_hash_from_key(DI_Key *k);
|
||||
internal B32 di_key_match(DI_Key *a, DI_Key *b);
|
||||
internal DI_Key di_key_copy(Arena *arena, DI_Key *src);
|
||||
internal DI_Key di_normalized_key_from_key(Arena *arena, DI_Key *src);
|
||||
internal void di_key_list_push(Arena *arena, DI_KeyList *list, DI_Key *key);
|
||||
internal DI_KeyArray di_key_array_from_list(Arena *arena, DI_KeyList *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void di_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scope Functions
|
||||
|
||||
internal DI_Scope *di_scope_open(void);
|
||||
internal void di_scope_close(DI_Scope *scope);
|
||||
internal void di_scope_touch_node__stripe_mutex_r_guarded(DI_Scope *scope, DI_Node *node);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Per-Slot Functions
|
||||
|
||||
internal DI_Node *di_node_from_key_slot__stripe_mutex_r_guarded(DI_Slot *slot, DI_Key *key);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Per-Stripe Functions
|
||||
|
||||
internal U64 di_string_bucket_idx_from_string_size(U64 size);
|
||||
internal String8 di_string_alloc__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string);
|
||||
internal void di_string_release__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Key Opening/Closing
|
||||
|
||||
internal void di_open(DI_Key *key);
|
||||
internal void di_close(DI_Key *key);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal RDI_Parsed *di_rdi_from_key(DI_Scope *scope, DI_Key *key, U64 endt_us);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parse Threads
|
||||
|
||||
internal B32 di_u2p_enqueue_key(DI_Key *key, U64 endt_us);
|
||||
internal void di_u2p_dequeue_key(Arena *arena, DI_Key *out_key);
|
||||
|
||||
internal void di_p2u_push_event(DI_Event *event);
|
||||
internal DI_EventList di_p2u_pop_events(Arena *arena, U64 endt_us);
|
||||
|
||||
internal void di_parse_thread__entry_point(void *p);
|
||||
|
||||
#endif // DI_H
|
||||
+158
-182
@@ -1,182 +1,158 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Generated Code
|
||||
|
||||
#include "generated/demon.meta.c"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Type Functions (Helpers, Implemented Once)
|
||||
|
||||
//- rjf: handles
|
||||
|
||||
internal DMN_Handle
|
||||
dmn_handle_zero(void)
|
||||
{
|
||||
DMN_Handle h = {0};
|
||||
return h;
|
||||
}
|
||||
|
||||
internal B32
|
||||
dmn_handle_match(DMN_Handle a, DMN_Handle b)
|
||||
{
|
||||
return a.u32[0] == b.u32[0] && a.u32[1] == b.u32[1];
|
||||
}
|
||||
|
||||
//- rjf: trap chunk lists
|
||||
|
||||
internal void
|
||||
dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap)
|
||||
{
|
||||
DMN_TrapChunkNode *node = list->last;
|
||||
if(node == 0 || node->count >= node->cap)
|
||||
{
|
||||
node = push_array(arena, DMN_TrapChunkNode, 1);
|
||||
node->cap = cap;
|
||||
node->v = push_array_no_zero(arena, DMN_Trap, node->cap);
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->node_count += 1;
|
||||
}
|
||||
MemoryCopyStruct(&node->v[node->count], trap);
|
||||
node->count += 1;
|
||||
list->trap_count += 1;
|
||||
}
|
||||
|
||||
internal void
|
||||
dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push)
|
||||
{
|
||||
if(dst->last == 0)
|
||||
{
|
||||
MemoryCopyStruct(dst, to_push);
|
||||
}
|
||||
else if(to_push->first != 0)
|
||||
{
|
||||
dst->last->next = to_push->first;
|
||||
dst->last = to_push->last;
|
||||
dst->node_count += to_push->node_count;
|
||||
dst->trap_count += to_push->trap_count;
|
||||
}
|
||||
MemoryZeroStruct(to_push);
|
||||
}
|
||||
|
||||
internal void
|
||||
dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push)
|
||||
{
|
||||
for(DMN_TrapChunkNode *src_n = to_push->first; src_n != 0; src_n = src_n->next)
|
||||
{
|
||||
DMN_TrapChunkNode *dst_n = push_array(arena, DMN_TrapChunkNode, 1);
|
||||
dst_n->v = src_n->v;
|
||||
dst_n->cap = src_n->cap;
|
||||
dst_n->count = src_n->count;
|
||||
SLLQueuePush(dst->first, dst->last, dst_n);
|
||||
dst->node_count += 1;
|
||||
dst->trap_count += dst_n->count;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: handle lists
|
||||
|
||||
internal void
|
||||
dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle)
|
||||
{
|
||||
DMN_HandleNode *node = push_array(arena, DMN_HandleNode, 1);
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
node->v = handle;
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
internal DMN_HandleArray
|
||||
dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list)
|
||||
{
|
||||
DMN_HandleArray array = {0};
|
||||
array.count = list->count;
|
||||
array.handles = push_array_no_zero(arena, DMN_Handle, array.count);
|
||||
U64 idx = 0;
|
||||
for(DMN_HandleNode *n = list->first; n != 0; n = n->next, idx += 1)
|
||||
{
|
||||
array.handles[idx] = n->v;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
internal DMN_HandleArray
|
||||
dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src)
|
||||
{
|
||||
DMN_HandleArray dst = {0};
|
||||
dst.count = src->count;
|
||||
dst.handles = push_array_no_zero(arena, DMN_Handle, dst.count);
|
||||
MemoryCopy(dst.handles, src->handles, sizeof(DMN_Handle)*dst.count);
|
||||
return dst;
|
||||
}
|
||||
|
||||
//- rjf: event list building
|
||||
|
||||
internal DMN_Event *
|
||||
dmn_event_list_push(Arena *arena, DMN_EventList *list)
|
||||
{
|
||||
DMN_EventNode *n = push_array(arena, DMN_EventNode, 1);
|
||||
SLLQueuePush(list->first, list->last, n);
|
||||
list->count += 1;
|
||||
DMN_Event *result = &n->v;
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once)
|
||||
|
||||
internal U64
|
||||
dmn_rip_from_thread(DMN_Handle thread)
|
||||
{
|
||||
U64 result = 0;
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
{
|
||||
Arch arch = dmn_arch_from_thread(thread);
|
||||
U64 reg_block_size = regs_block_size_from_arch(arch);
|
||||
void *reg_block = push_array(scratch.arena, U8, reg_block_size);
|
||||
dmn_thread_read_reg_block(thread, reg_block);
|
||||
result = regs_rip_from_arch_block(arch, reg_block);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
dmn_rsp_from_thread(DMN_Handle thread)
|
||||
{
|
||||
U64 result = 0;
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
{
|
||||
Arch arch = dmn_arch_from_thread(thread);
|
||||
U64 reg_block_size = regs_block_size_from_arch(arch);
|
||||
void *reg_block = push_array(scratch.arena, U8, reg_block_size);
|
||||
dmn_thread_read_reg_block(thread, reg_block);
|
||||
result = regs_rsp_from_arch_block(arch, reg_block);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Process Reading Helper Functions (Helpers, Implemented Once)
|
||||
|
||||
internal String8
|
||||
dmn_process_read_cstring(Arena *arena, DMN_Handle process, U64 addr)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8List block_list = {0};
|
||||
for(U64 cursor = addr, stride = 256; ; cursor += stride)
|
||||
{
|
||||
U8 *raw_block = push_array_no_zero(scratch.arena, U8, stride);
|
||||
U64 read_size = dmn_process_read(process, 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;
|
||||
}
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Generated Code
|
||||
|
||||
#include "generated/demon.meta.c"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Type Functions (Helpers, Implemented Once)
|
||||
|
||||
//- rjf: handles
|
||||
|
||||
internal DMN_Handle
|
||||
dmn_handle_zero(void)
|
||||
{
|
||||
DMN_Handle h = {0};
|
||||
return h;
|
||||
}
|
||||
|
||||
internal B32
|
||||
dmn_handle_match(DMN_Handle a, DMN_Handle b)
|
||||
{
|
||||
return a.u32[0] == b.u32[0] && a.u32[1] == b.u32[1];
|
||||
}
|
||||
|
||||
//- rjf: trap chunk lists
|
||||
|
||||
internal void
|
||||
dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap)
|
||||
{
|
||||
DMN_TrapChunkNode *node = list->last;
|
||||
if(node == 0 || node->count >= node->cap)
|
||||
{
|
||||
node = push_array(arena, DMN_TrapChunkNode, 1);
|
||||
node->cap = cap;
|
||||
node->v = push_array_no_zero(arena, DMN_Trap, node->cap);
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->node_count += 1;
|
||||
}
|
||||
MemoryCopyStruct(&node->v[node->count], trap);
|
||||
node->count += 1;
|
||||
list->trap_count += 1;
|
||||
}
|
||||
|
||||
internal void
|
||||
dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push)
|
||||
{
|
||||
if(dst->last == 0)
|
||||
{
|
||||
MemoryCopyStruct(dst, to_push);
|
||||
}
|
||||
else if(to_push->first != 0)
|
||||
{
|
||||
dst->last->next = to_push->first;
|
||||
dst->last = to_push->last;
|
||||
dst->node_count += to_push->node_count;
|
||||
dst->trap_count += to_push->trap_count;
|
||||
}
|
||||
MemoryZeroStruct(to_push);
|
||||
}
|
||||
|
||||
internal void
|
||||
dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push)
|
||||
{
|
||||
for(DMN_TrapChunkNode *src_n = to_push->first; src_n != 0; src_n = src_n->next)
|
||||
{
|
||||
DMN_TrapChunkNode *dst_n = push_array(arena, DMN_TrapChunkNode, 1);
|
||||
dst_n->v = src_n->v;
|
||||
dst_n->cap = src_n->cap;
|
||||
dst_n->count = src_n->count;
|
||||
SLLQueuePush(dst->first, dst->last, dst_n);
|
||||
dst->node_count += 1;
|
||||
dst->trap_count += dst_n->count;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: handle lists
|
||||
|
||||
internal void
|
||||
dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle)
|
||||
{
|
||||
DMN_HandleNode *node = push_array(arena, DMN_HandleNode, 1);
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
node->v = handle;
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
internal DMN_HandleArray
|
||||
dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list)
|
||||
{
|
||||
DMN_HandleArray array = {0};
|
||||
array.count = list->count;
|
||||
array.handles = push_array_no_zero(arena, DMN_Handle, array.count);
|
||||
U64 idx = 0;
|
||||
for(DMN_HandleNode *n = list->first; n != 0; n = n->next, idx += 1)
|
||||
{
|
||||
array.handles[idx] = n->v;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
internal DMN_HandleArray
|
||||
dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src)
|
||||
{
|
||||
DMN_HandleArray dst = {0};
|
||||
dst.count = src->count;
|
||||
dst.handles = push_array_no_zero(arena, DMN_Handle, dst.count);
|
||||
MemoryCopy(dst.handles, src->handles, sizeof(DMN_Handle)*dst.count);
|
||||
return dst;
|
||||
}
|
||||
|
||||
//- rjf: event list building
|
||||
|
||||
internal DMN_Event *
|
||||
dmn_event_list_push(Arena *arena, DMN_EventList *list)
|
||||
{
|
||||
DMN_EventNode *n = push_array(arena, DMN_EventNode, 1);
|
||||
SLLQueuePush(list->first, list->last, n);
|
||||
list->count += 1;
|
||||
DMN_Event *result = &n->v;
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once)
|
||||
|
||||
internal U64
|
||||
dmn_rip_from_thread(DMN_Handle thread)
|
||||
{
|
||||
U64 result = 0;
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
{
|
||||
Architecture arch = dmn_arch_from_thread(thread);
|
||||
U64 reg_block_size = regs_block_size_from_architecture(arch);
|
||||
void *reg_block = push_array(scratch.arena, U8, reg_block_size);
|
||||
dmn_thread_read_reg_block(thread, reg_block);
|
||||
result = regs_rip_from_arch_block(arch, reg_block);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
dmn_rsp_from_thread(DMN_Handle thread)
|
||||
{
|
||||
U64 result = 0;
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
{
|
||||
Architecture arch = dmn_arch_from_thread(thread);
|
||||
U64 reg_block_size = regs_block_size_from_architecture(arch);
|
||||
void *reg_block = push_array(scratch.arena, U8, reg_block_size);
|
||||
dmn_thread_read_reg_block(thread, reg_block);
|
||||
result = regs_rsp_from_arch_block(arch, reg_block);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
+237
-253
@@ -1,253 +1,237 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DEMON_CORE_H
|
||||
#define DEMON_CORE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Control-Thread-Only Context
|
||||
//
|
||||
// An instance of this struct must ONLY be returned by dmn_ctrl_begin, and only
|
||||
// used by the thread which called it. All APIs which can ONLY run on the
|
||||
// control thread, which blocks to control & receive events, will take this
|
||||
// parameter. All other APIs can be called from any thread.
|
||||
|
||||
typedef struct DMN_CtrlCtx DMN_CtrlCtx;
|
||||
struct DMN_CtrlCtx
|
||||
{
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Handle Types
|
||||
|
||||
typedef union DMN_Handle DMN_Handle;
|
||||
union DMN_Handle
|
||||
{
|
||||
U32 u32[2];
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct DMN_HandleNode DMN_HandleNode;
|
||||
struct DMN_HandleNode
|
||||
{
|
||||
DMN_HandleNode *next;
|
||||
DMN_Handle v;
|
||||
};
|
||||
|
||||
typedef struct DMN_HandleList DMN_HandleList;
|
||||
struct DMN_HandleList
|
||||
{
|
||||
DMN_HandleNode *first;
|
||||
DMN_HandleNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DMN_HandleArray DMN_HandleArray;
|
||||
struct DMN_HandleArray
|
||||
{
|
||||
DMN_Handle *handles;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Generated Code
|
||||
|
||||
#include "generated/demon.meta.h"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Event Types
|
||||
|
||||
typedef struct DMN_Event DMN_Event;
|
||||
struct DMN_Event
|
||||
{
|
||||
DMN_EventKind kind;
|
||||
DMN_ErrorKind error_kind;
|
||||
DMN_MemoryEventKind memory_kind;
|
||||
DMN_ExceptionKind exception_kind;
|
||||
DMN_Handle process;
|
||||
DMN_Handle thread;
|
||||
DMN_Handle module;
|
||||
Arch arch;
|
||||
U64 address;
|
||||
U64 size;
|
||||
String8 string;
|
||||
U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully)
|
||||
U32 flags; // DMN_TrapFlags, if `DMN_EventKind_SetBreakpoint`
|
||||
S32 signo;
|
||||
S32 sigcode;
|
||||
U64 instruction_pointer;
|
||||
U64 stack_pointer;
|
||||
U64 user_data;
|
||||
B32 exception_repeated;
|
||||
};
|
||||
|
||||
typedef struct DMN_EventNode DMN_EventNode;
|
||||
struct DMN_EventNode
|
||||
{
|
||||
DMN_EventNode *next;
|
||||
DMN_Event v;
|
||||
};
|
||||
|
||||
typedef struct DMN_EventList DMN_EventList;
|
||||
struct DMN_EventList
|
||||
{
|
||||
DMN_EventNode *first;
|
||||
DMN_EventNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Run Control Types
|
||||
|
||||
typedef U32 DMN_TrapFlags;
|
||||
enum
|
||||
{
|
||||
DMN_TrapFlag_BreakOnWrite = (1<<0),
|
||||
DMN_TrapFlag_BreakOnRead = (1<<1),
|
||||
DMN_TrapFlag_BreakOnExecute = (1<<2),
|
||||
};
|
||||
|
||||
typedef struct DMN_Trap DMN_Trap;
|
||||
struct DMN_Trap
|
||||
{
|
||||
DMN_Handle process;
|
||||
U64 vaddr;
|
||||
U64 id;
|
||||
DMN_TrapFlags flags;
|
||||
U32 size;
|
||||
};
|
||||
|
||||
typedef struct DMN_TrapChunkNode DMN_TrapChunkNode;
|
||||
struct DMN_TrapChunkNode
|
||||
{
|
||||
DMN_TrapChunkNode *next;
|
||||
DMN_Trap *v;
|
||||
U64 cap;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DMN_TrapChunkList DMN_TrapChunkList;
|
||||
struct DMN_TrapChunkList
|
||||
{
|
||||
DMN_TrapChunkNode *first;
|
||||
DMN_TrapChunkNode *last;
|
||||
U64 node_count;
|
||||
U64 trap_count;
|
||||
};
|
||||
|
||||
typedef struct DMN_RunCtrls DMN_RunCtrls;
|
||||
struct DMN_RunCtrls
|
||||
{
|
||||
DMN_Handle priority_thread;
|
||||
DMN_Handle single_step_thread;
|
||||
B8 ignore_previous_exception;
|
||||
B8 run_entities_are_unfrozen;
|
||||
B8 run_entities_are_processes;
|
||||
DMN_Handle *run_entities;
|
||||
U64 run_entity_count;
|
||||
DMN_TrapChunkList traps;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: System Process Listing Types
|
||||
|
||||
typedef struct DMN_ProcessIter DMN_ProcessIter;
|
||||
struct DMN_ProcessIter
|
||||
{
|
||||
U64 v[2];
|
||||
};
|
||||
|
||||
typedef struct DMN_ProcessInfo DMN_ProcessInfo;
|
||||
struct DMN_ProcessInfo
|
||||
{
|
||||
String8 name;
|
||||
U32 pid;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Type Functions (Helpers, Implemented Once)
|
||||
|
||||
//- rjf: handles
|
||||
internal DMN_Handle dmn_handle_zero(void);
|
||||
internal B32 dmn_handle_match(DMN_Handle a, DMN_Handle b);
|
||||
|
||||
//- rjf: trap chunk lists
|
||||
internal void dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap);
|
||||
internal void dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push);
|
||||
internal void dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push);
|
||||
|
||||
//- rjf: handle lists
|
||||
internal void dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle);
|
||||
internal DMN_HandleArray dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list);
|
||||
internal DMN_HandleArray dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src);
|
||||
|
||||
//- rjf: event list building
|
||||
internal DMN_Event *dmn_event_list_push(Arena *arena, DMN_EventList *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once)
|
||||
|
||||
internal U64 dmn_rip_from_thread(DMN_Handle thread);
|
||||
internal U64 dmn_rsp_from_thread(DMN_Handle thread);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Process Reading Helper Functions (Helpers, Implemented Once)
|
||||
|
||||
internal String8 dmn_process_read_cstring(Arena *arena, DMN_Handle process, U64 addr);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS)
|
||||
|
||||
internal void dmn_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @dmn_os_hooks Blocking Control Thread Operations (Implemented Per-OS)
|
||||
|
||||
internal DMN_CtrlCtx *dmn_ctrl_begin(void);
|
||||
internal void dmn_ctrl_exclusive_access_begin(void);
|
||||
internal void dmn_ctrl_exclusive_access_end(void);
|
||||
#define DMN_CtrlExclusiveAccessScope DeferLoop(dmn_ctrl_exclusive_access_begin(), dmn_ctrl_exclusive_access_end())
|
||||
internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params);
|
||||
internal B32 dmn_ctrl_attach(DMN_CtrlCtx *ctx, U32 pid);
|
||||
internal B32 dmn_ctrl_kill(DMN_CtrlCtx *ctx, DMN_Handle process, U32 exit_code);
|
||||
internal B32 dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process);
|
||||
internal DMN_EventList dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @dmn_os_hooks Halting (Implemented Per-OS)
|
||||
|
||||
internal void dmn_halt(U64 code, U64 user_data);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @dmn_os_hooks Introspection Functions (Implemented Per-OS)
|
||||
|
||||
//- rjf: non-blocking-control-thread access barriers
|
||||
internal B32 dmn_access_open(void);
|
||||
internal void dmn_access_close(void);
|
||||
#define DMN_AccessScope DeferLoopChecked(dmn_access_open(), dmn_access_close())
|
||||
|
||||
//- rjf: processes
|
||||
internal U64 dmn_process_memory_reserve(DMN_Handle process, U64 vaddr, U64 size);
|
||||
internal void dmn_process_memory_commit(DMN_Handle process, U64 vaddr, U64 size);
|
||||
internal void dmn_process_memory_decommit(DMN_Handle process, U64 vaddr, U64 size);
|
||||
internal void dmn_process_memory_release(DMN_Handle process, U64 vaddr, U64 size);
|
||||
internal void dmn_process_memory_protect(DMN_Handle process, U64 vaddr, U64 size, OS_AccessFlags flags);
|
||||
internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst);
|
||||
internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src);
|
||||
#define dmn_process_read_struct(process, vaddr, ptr) dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
|
||||
#define dmn_process_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
|
||||
|
||||
//- rjf: threads
|
||||
internal Arch dmn_arch_from_thread(DMN_Handle handle);
|
||||
internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle handle);
|
||||
internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle);
|
||||
internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block);
|
||||
internal B32 dmn_thread_write_reg_block(DMN_Handle handle, void *reg_block);
|
||||
|
||||
//- rjf: system process listing
|
||||
internal void dmn_process_iter_begin(DMN_ProcessIter *iter);
|
||||
internal B32 dmn_process_iter_next(Arena *arena, DMN_ProcessIter *iter, DMN_ProcessInfo *info_out);
|
||||
internal void dmn_process_iter_end(DMN_ProcessIter *iter);
|
||||
|
||||
#endif // DEMON_CORE_H
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DEMON_CORE_H
|
||||
#define DEMON_CORE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Control-Thread-Only Context
|
||||
//
|
||||
// An instance of this struct must ONLY be returned by dmn_ctrl_begin, and only
|
||||
// used by the thread which called it. All APIs which can ONLY run on the
|
||||
// control thread, which blocks to control & receive events, will take this
|
||||
// parameter. All other APIs can be called from any thread.
|
||||
|
||||
typedef struct DMN_CtrlCtx DMN_CtrlCtx;
|
||||
struct DMN_CtrlCtx
|
||||
{
|
||||
U64 u64 [1];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Handle Types
|
||||
|
||||
typedef union DMN_Handle DMN_Handle;
|
||||
union DMN_Handle
|
||||
{
|
||||
U32 u32[2];
|
||||
U64 u64[1];
|
||||
};
|
||||
|
||||
typedef struct DMN_HandleNode DMN_HandleNode;
|
||||
struct DMN_HandleNode
|
||||
{
|
||||
DMN_HandleNode *next;
|
||||
DMN_Handle v;
|
||||
};
|
||||
|
||||
typedef struct DMN_HandleList DMN_HandleList;
|
||||
struct DMN_HandleList
|
||||
{
|
||||
DMN_HandleNode *first;
|
||||
DMN_HandleNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DMN_HandleArray DMN_HandleArray;
|
||||
struct DMN_HandleArray
|
||||
{
|
||||
DMN_Handle *handles;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Generated Code
|
||||
|
||||
#include "generated/demon.meta.h"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Event Types
|
||||
|
||||
typedef struct DMN_Event DMN_Event;
|
||||
struct DMN_Event
|
||||
{
|
||||
DMN_EventKind kind;
|
||||
DMN_ErrorKind error_kind;
|
||||
DMN_MemoryEventKind memory_kind;
|
||||
DMN_ExceptionKind exception_kind;
|
||||
DMN_Handle process;
|
||||
DMN_Handle thread;
|
||||
DMN_Handle module;
|
||||
Architecture arch;
|
||||
U64 address;
|
||||
U64 size;
|
||||
String8 string;
|
||||
U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully)
|
||||
U32 flags;
|
||||
S32 signo;
|
||||
S32 sigcode;
|
||||
U64 instruction_pointer;
|
||||
U64 stack_pointer;
|
||||
U64 user_data;
|
||||
B32 exception_repeated;
|
||||
};
|
||||
|
||||
typedef struct DMN_EventNode DMN_EventNode;
|
||||
struct DMN_EventNode
|
||||
{
|
||||
DMN_EventNode *next;
|
||||
DMN_Event v;
|
||||
};
|
||||
|
||||
typedef struct DMN_EventList DMN_EventList;
|
||||
struct DMN_EventList
|
||||
{
|
||||
DMN_EventNode *first;
|
||||
DMN_EventNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Run Control Types
|
||||
|
||||
typedef struct DMN_Trap DMN_Trap;
|
||||
struct DMN_Trap
|
||||
{
|
||||
DMN_Handle process;
|
||||
U64 vaddr;
|
||||
U64 id;
|
||||
};
|
||||
|
||||
typedef struct DMN_TrapChunkNode DMN_TrapChunkNode;
|
||||
struct DMN_TrapChunkNode
|
||||
{
|
||||
DMN_TrapChunkNode *next;
|
||||
DMN_Trap *v;
|
||||
U64 cap;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DMN_TrapChunkList DMN_TrapChunkList;
|
||||
struct DMN_TrapChunkList
|
||||
{
|
||||
DMN_TrapChunkNode *first;
|
||||
DMN_TrapChunkNode *last;
|
||||
U64 node_count;
|
||||
U64 trap_count;
|
||||
};
|
||||
|
||||
typedef struct DMN_RunCtrls DMN_RunCtrls;
|
||||
struct DMN_RunCtrls
|
||||
{
|
||||
DMN_Handle single_step_thread;
|
||||
B8 ignore_previous_exception;
|
||||
B8 run_entities_are_unfrozen;
|
||||
B8 run_entities_are_processes;
|
||||
DMN_Handle *run_entities;
|
||||
U64 run_entity_count;
|
||||
DMN_TrapChunkList traps;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: System Process Listing Types
|
||||
|
||||
typedef struct DMN_ProcessIter DMN_ProcessIter;
|
||||
struct DMN_ProcessIter
|
||||
{
|
||||
U64 v[2];
|
||||
};
|
||||
|
||||
typedef struct DMN_ProcessInfo DMN_ProcessInfo;
|
||||
struct DMN_ProcessInfo
|
||||
{
|
||||
String8 name;
|
||||
U32 pid;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Type Functions (Helpers, Implemented Once)
|
||||
|
||||
//- rjf: handles
|
||||
internal DMN_Handle dmn_handle_zero(void);
|
||||
internal B32 dmn_handle_match(DMN_Handle a, DMN_Handle b);
|
||||
|
||||
//- rjf: trap chunk lists
|
||||
internal void dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap);
|
||||
internal void dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push);
|
||||
internal void dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push);
|
||||
|
||||
//- rjf: handle lists
|
||||
internal void dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle);
|
||||
internal DMN_HandleArray dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list);
|
||||
internal DMN_HandleArray dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src);
|
||||
|
||||
//- rjf: event list building
|
||||
internal DMN_Event *dmn_event_list_push(Arena *arena, DMN_EventList *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once)
|
||||
|
||||
internal U64 dmn_rip_from_thread(DMN_Handle thread);
|
||||
internal U64 dmn_rsp_from_thread(DMN_Handle thread);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS)
|
||||
|
||||
internal void dmn_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @dmn_os_hooks Blocking Control Thread Operations (Implemented Per-OS)
|
||||
|
||||
internal DMN_CtrlCtx *dmn_ctrl_begin(void);
|
||||
internal void dmn_ctrl_exclusive_access_begin(void);
|
||||
internal void dmn_ctrl_exclusive_access_end(void);
|
||||
#define DMN_CtrlExclusiveAccessScope DeferLoop(dmn_ctrl_exclusive_access_begin(), dmn_ctrl_exclusive_access_end())
|
||||
internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_LaunchOptions *options);
|
||||
internal B32 dmn_ctrl_attach(DMN_CtrlCtx *ctx, U32 pid);
|
||||
internal B32 dmn_ctrl_kill(DMN_CtrlCtx *ctx, DMN_Handle process, U32 exit_code);
|
||||
internal B32 dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process);
|
||||
internal DMN_EventList dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @dmn_os_hooks Halting (Implemented Per-OS)
|
||||
|
||||
internal void dmn_halt(U64 code, U64 user_data);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @dmn_os_hooks Introspection Functions (Implemented Per-OS)
|
||||
|
||||
//- rjf: run/memory/register counters
|
||||
internal U64 dmn_run_gen(void);
|
||||
internal U64 dmn_mem_gen(void);
|
||||
internal U64 dmn_reg_gen(void);
|
||||
|
||||
//- rjf: non-blocking-control-thread access barriers
|
||||
internal B32 dmn_access_open(void);
|
||||
internal void dmn_access_close(void);
|
||||
#define DMN_AccessScope DeferLoopChecked(dmn_access_open(), dmn_access_close())
|
||||
|
||||
//- rjf: processes
|
||||
internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst);
|
||||
internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src);
|
||||
#define dmn_process_read_struct(process, vaddr, ptr) dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
|
||||
#define dmn_process_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
|
||||
|
||||
//- rjf: threads
|
||||
internal Architecture dmn_arch_from_thread(DMN_Handle handle);
|
||||
internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle handle);
|
||||
internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle);
|
||||
internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block);
|
||||
internal B32 dmn_thread_write_reg_block(DMN_Handle handle, void *reg_block);
|
||||
|
||||
//- rjf: system process listing
|
||||
internal void dmn_process_iter_begin(DMN_ProcessIter *iter);
|
||||
internal B32 dmn_process_iter_next(Arena *arena, DMN_ProcessIter *iter, DMN_ProcessInfo *info_out);
|
||||
internal void dmn_process_iter_end(DMN_ProcessIter *iter);
|
||||
|
||||
#endif // DEMON_CORE_H
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user