mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-05-17 16:46:59 +02:00
[docs] add CodingStyle + Coding guidelines, HOS kernel basics, 'Settings' and add external resources, add better docs for dtrace-tool.pl (#3964)
Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3964 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
This commit is contained in:
parent
4d49341918
commit
8330940eca
12 changed files with 614 additions and 495 deletions
14
docs/Deps.md
14
docs/Deps.md
|
|
@ -35,6 +35,11 @@ If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6,
|
||||||
|
|
||||||
* For help setting up Qt Creator, run `./install.sh -h qtcreator`
|
* For help setting up Qt Creator, run `./install.sh -h qtcreator`
|
||||||
|
|
||||||
|
* If you're using clang-cl and want to still use MSVC
|
||||||
|
* Check the option to add "C++ clang compiler for Windows" on Visual Studio installer and uncheck "x64/x86 build tool for MSVC" while selecting "C++ desktop developement tools" and change Visual Studio to 2026, from 2022.
|
||||||
|
* At qt creator section generator tab change Visual Studio 17 2022 to 2026.
|
||||||
|
* Finally, to use clang-cl: `cmake -S . -B build -G "Visual Studio 17 2026" -T ClangCL`
|
||||||
|
|
||||||
If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go [back home](Build.md) and continue.
|
If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go [back home](Build.md) and continue.
|
||||||
|
|
||||||
## Externals
|
## Externals
|
||||||
|
|
@ -264,7 +269,10 @@ If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
||||||
<details>
|
<details>
|
||||||
<summary>NetBSD</summary>
|
<summary>NetBSD</summary>
|
||||||
|
|
||||||
For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq libopus qt6 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx`.
|
For NetBSD +10.1:
|
||||||
|
```sh
|
||||||
|
pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq libopus qt6 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx frozen
|
||||||
|
```
|
||||||
|
|
||||||
[Caveats](./Caveats.md#netbsd).
|
[Caveats](./Caveats.md#netbsd).
|
||||||
|
|
||||||
|
|
@ -274,7 +282,7 @@ For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pkg_add -u
|
pkg_add -u
|
||||||
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1-1.0.29
|
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1-1.0.29 quazip-qt6
|
||||||
```
|
```
|
||||||
|
|
||||||
[Caveats](./Caveats.md#openbsd).
|
[Caveats](./Caveats.md#openbsd).
|
||||||
|
|
@ -344,7 +352,7 @@ pacman -Syuu --needed --noconfirm $packages
|
||||||
<summary>HaikuOS</summary>
|
<summary>HaikuOS</summary>
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel
|
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel cubeb_devel simpleini quazip_qt6_devel
|
||||||
```
|
```
|
||||||
|
|
||||||
[Caveats](./Caveats.md#haikuos).
|
[Caveats](./Caveats.md#haikuos).
|
||||||
|
|
|
||||||
31
docs/HosKernel.md
Normal file
31
docs/HosKernel.md
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# HOS Kernel
|
||||||
|
|
||||||
|
In brief, the HOS kernel is a microkernel, some services and programs run in userspace, the primary way to do communication between these is via `HIPC` (not covered here); otherwise most of the primitives reside in the forms of syscalls invoked via `svc #imm`. The kernel supports both 32-bit and 64-bit programs, and has the capacity to use 32, 36 and 39 bits of address space for spawned processes. Most of the networking stack is based off FreeBSD's network stack.
|
||||||
|
|
||||||
|
The emulator implements the majority of the syscalls pertaining to the HOS kernel itself. When we talk about the HOS Kernel (in the context of the emulator) we are strictly speaking about the mechanisms from which syscalls are handled (and it's subsequent side effects, such as the page table book-keeping). The emulator at it's current state is unable to load a custom low-level kernel and do supervisor-level emulation.
|
||||||
|
|
||||||
|
Most programs in NX eventually invoke an `svc`, which, depending on it's immediate value, will go on to be dispatched into one of the specific syscall handlers.
|
||||||
|
|
||||||
|
These can be seen in [svc.cpp](/src/core/hle/kernel/svc.cpp). All of these correspond to syscalls which userspace programs may perform.
|
||||||
|
|
||||||
|
In turn, these syscalls create the mechanisms that allows programs to use CMIF/TIPC as their primary IPC form to contact other services/processes running on the system, the details of which will not be covered here, but you can consult the relevant [SwitchBrew article: 'HIPC'](https://switchbrew.org/wiki/HIPC).
|
||||||
|
|
||||||
|
From the point of view of the programs, no special devices (such as PCIE, Realtek drivers, Bluetooth or USB) has to be handled by the emulator; this is because most of the fun occurs in specialized services such as `usb:u` or `pcie` services. Which aren't emulated (yet).
|
||||||
|
|
||||||
|
Due to the nature of syscalls, many of them interact with memory. The emulated kernel has an internal tree-like structure, borrowed from FreeBSD's intrusive red-black tree; this is used to track and find mappings added or removed. Thus most of the process space is emulated in this way.
|
||||||
|
|
||||||
|
The kernel keeps it's own separate pagetable, in a traditional sense, each process has it's own pagetable, this is true for HOS as well.
|
||||||
|
|
||||||
|
Every process keeps it's own tracking of the following structures:
|
||||||
|
- Name (13 characters)
|
||||||
|
- 64-bit ID
|
||||||
|
- A handle table
|
||||||
|
- Exclusive monitor
|
||||||
|
- Threads
|
||||||
|
- Held locks
|
||||||
|
- Thread local pages
|
||||||
|
- A page table for each process
|
||||||
|
|
||||||
|
The emulator willingly restricts itself to only use 4 threads (to emulate 4 cores), this is because most existing applications do not benefit greatly from the added core count, and in fact can be detrimental due to extra contention. This translates equitatively to about 4 `ArmInterface` slots for each process, these are then redirected to whatever is the last `pc` of the last thread running on the core is meant to be; proceed to run it, then when returning (due to halt or interruption), proceed to reschedule the thread.
|
||||||
|
|
||||||
|
The scheduler as-is isn't 100% faithful to the original, and has great timing variance (especially due to the fact the emulator can run in systems with wildly different timings).
|
||||||
|
|
@ -853,6 +853,8 @@ Texture Query.
|
||||||
|
|
||||||
Vote Across SIMD Thread Group
|
Vote Across SIMD Thread Group
|
||||||
|
|
||||||
|
`VOTE_vtg` is a kepler leftover.
|
||||||
|
|
||||||
# VSET
|
# VSET
|
||||||
`0100 000- ---- ----`
|
`0100 000- ---- ----`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,26 @@
|
||||||
|
|
||||||
Are you just a casual user? Take a look at our [User Handbook](./user) then!
|
Are you just a casual user? Take a look at our [User Handbook](./user) then!
|
||||||
|
|
||||||
|
If you want to register/signup as a contributor, take a gander at the [signup guide](./SIGNUP.md).
|
||||||
|
|
||||||
This contains documentation created by developers. This contains build instructions, guidelines, instructions/layouts for [cool stuff we made](./CPMUtil), and more.
|
This contains documentation created by developers. This contains build instructions, guidelines, instructions/layouts for [cool stuff we made](./CPMUtil), and more.
|
||||||
|
|
||||||
- **[General Build Instructions](Build.md)**
|
- **[General Build Instructions](./Build.md)**
|
||||||
- **[CMake Options](Options.md)**
|
- **[CMake Options](./Options.md)**
|
||||||
- **[Cross Compiling](CrossCompile.md)**
|
- **[Cross Compiling](./CrossCompile.md)**
|
||||||
- **[Development Guidelines](Development.md)**
|
- **[Development Guidelines](./Development.md)**
|
||||||
- **[Dependencies](Deps.md)**
|
- **[Dependencies](./Deps.md)**
|
||||||
- **[Debug Guidelines](./Debug.md)**
|
- **[Debug Guidelines](./Debug.md)**
|
||||||
- **[CPM - CMake Package Manager](./CPMUtil)**
|
- **[CPM - CMake Package Manager](./CPMUtil)**
|
||||||
- **[Platform-Specific Caveats](Caveats.md)**
|
- **[Platform-Specific Caveats](./Caveats.md)**
|
||||||
- **[The NVIDIA SM86 (Maxwell) GPU](./NvidiaGpu.md)**
|
- **[The NVIDIA SM86 (Maxwell) GPU](./NvidiaGpu.md)**
|
||||||
- **[Dynarmic](./dynarmic)**
|
|
||||||
- **[Cross compilation](./CrossCompile.md)**
|
- **[Cross compilation](./CrossCompile.md)**
|
||||||
- **[Driver Bugs](./DriverBugs.md)**
|
- **[Driver Bugs](./DriverBugs.md)**
|
||||||
- **[Building Older Commits](./build/OlderCommits.md)**
|
- **[Building Older Commits](./build/OlderCommits.md)**
|
||||||
|
- Subsystems:
|
||||||
|
- **[Dynarmic](./dynarmic/README.md)**
|
||||||
|
- **[HOS Kernel](./HosKernel.md)**
|
||||||
|
- **[Settings](./Settings.md)**
|
||||||
|
|
||||||
## Policies
|
## Policies
|
||||||
|
|
||||||
|
|
@ -25,3 +30,12 @@ Policies and information on development.
|
||||||
- **[AI and LLM Usage](./policies/AI.md)**
|
- **[AI and LLM Usage](./policies/AI.md)**
|
||||||
- **[Release Policy](./policies/Release.md)**
|
- **[Release Policy](./policies/Release.md)**
|
||||||
- **[Coding guidelines](./policies/Coding.md)**
|
- **[Coding guidelines](./policies/Coding.md)**
|
||||||
|
- **[Coding Style guidelines](./policies/CodingStyle.md)**
|
||||||
|
|
||||||
|
## Externals
|
||||||
|
|
||||||
|
Other useful resources in general, take a quick read if you need.
|
||||||
|
|
||||||
|
- **[SwitchBrew](https://switchbrew.org/wiki/Main_Page)**
|
||||||
|
- **[IPS file format](https://zerosoft.zophar.net/ips.php)**
|
||||||
|
- **[IPSwitch file format](https://github.com/3096/ipswitch)**
|
||||||
|
|
|
||||||
327
docs/Settings.md
Normal file
327
docs/Settings.md
Normal file
|
|
@ -0,0 +1,327 @@
|
||||||
|
# Settings
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> This guide is intended for developers ONLY. If you're looking for configuring the emulator itself, please read **[the user handbook](./user/README.md)**.
|
||||||
|
|
||||||
|
Settings on the emulator are very important, toggles and such can be used to guard and/or add branches to paths where some games may crash while others won't, and viceversa.
|
||||||
|
|
||||||
|
However, this process can be tedious for those unfamiliar; this document serves as a outline/documentation for the settings subsystem.
|
||||||
|
|
||||||
|
## Index
|
||||||
|
|
||||||
|
* [Adding Debug Knobs](#adding-debug-knobs)
|
||||||
|
* [Advantages](#advantages)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Accessing Debug Knobs (dev side)](#accessing-debug-knobs-dev-side)
|
||||||
|
* [Setting Debug Knobs (user side)](#setting-debug-knobs-user-side)
|
||||||
|
* [Bit Manipulation Examples](#bit-manipulation-examples)
|
||||||
|
* [Terminology and user communication](#terminology-and-user-communication)
|
||||||
|
* [Examples](#examples)
|
||||||
|
* [Example 1: Conditional Debug Logging](#example-1-conditional-debug-logging)
|
||||||
|
* [Example 2: Performance Tuning](#example-2-performance-tuning)
|
||||||
|
* [Example 3: Feature Gating](#example-3-feature-gating)
|
||||||
|
* [Best Practices](#best-practices)
|
||||||
|
* [Adding Boolean Settings Toggles](#adding-boolean-settings-toggles)
|
||||||
|
* [Step 1 - Common Setting](#step-1-common-setting)
|
||||||
|
* [Step 2 - Qt Toggle](#step-2-qt-toggle)
|
||||||
|
* [Step 3 - Kotlin (Android)](#step-3-kotlin-android)
|
||||||
|
* [Step 3.1 - BooleanSetting.kt](#step-3-1-booleansetting-kt)
|
||||||
|
* [Step 3.2 - SettingsItem.kt](#step-3-2-settingsitem-kt)
|
||||||
|
* [Step 3.3 - SettingsFragmentPresenter.kt](#step-3-3-settingsfragmentpresenter-kt)
|
||||||
|
* [Step 3.4 - Localization](#step-3-4-localization)
|
||||||
|
* [Step 4 - Use Your Toggle](#step-4-use-your-toggle)
|
||||||
|
* [Best Practices](#best-practices)
|
||||||
|
|
||||||
|
## Adding Boolean Settings Toggles
|
||||||
|
|
||||||
|
This guide will walk you through adding a new boolean toggle setting to Eden's configuration across both Qt's (PC) and Kotlin's (Android) UIs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 1 - Common Setting
|
||||||
|
|
||||||
|
Firstly add your desired toggle:
|
||||||
|
|
||||||
|
Example: `src/common/setting.h`
|
||||||
|
```cpp
|
||||||
|
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
||||||
|
```
|
||||||
|
|
||||||
|
Remember to add your toggle to the appropriate category, for example:
|
||||||
|
|
||||||
|
Common Categories:
|
||||||
|
|
||||||
|
* Category::Renderer
|
||||||
|
* Category::RendererAdvanced
|
||||||
|
* Category::RendererExtensions
|
||||||
|
* Category::System
|
||||||
|
* Category::Core
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> If you wish for your toggle to be `on by default` then change `false` to `true` after `linkage,`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 2 - Qt Toggle
|
||||||
|
|
||||||
|
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
||||||
|
|
||||||
|
Example: `src/qt_common/config/shared_translation.cpp`
|
||||||
|
```cpp
|
||||||
|
INSERT(Settings,
|
||||||
|
your_setting_name,
|
||||||
|
tr("Your Setting Display Name"),
|
||||||
|
tr("Detailed description of what this setting does.\n"
|
||||||
|
"You can use multiple lines.\n"
|
||||||
|
"Explain any caveats or requirements."));
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Make sure to:
|
||||||
|
|
||||||
|
* Keep display naming consistant
|
||||||
|
* Put detailed info in the description
|
||||||
|
* Use `\n` for line breaks in descriptions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 3 - Kotlin (Android)
|
||||||
|
|
||||||
|
#### Step 3.1 - BooleanSetting.kt
|
||||||
|
|
||||||
|
Add where it should be in the settings.
|
||||||
|
|
||||||
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
||||||
|
```kts
|
||||||
|
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Make sure to:
|
||||||
|
|
||||||
|
* Ensure the prefix naming matches the intended category.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Step 3.2 - SettingsItem.kt
|
||||||
|
|
||||||
|
Add the toggle to the Kotlin (Android) UI
|
||||||
|
|
||||||
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
||||||
|
```kts
|
||||||
|
put(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.RENDERER_YOUR_SETTING_NAME,
|
||||||
|
titleId = R.string.your_setting_name,
|
||||||
|
descriptionId = R.string.your_setting_name_description
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Step 3.3 - SettingsFragmentPresenter.kt
|
||||||
|
|
||||||
|
Add your setting within the right category.
|
||||||
|
|
||||||
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
||||||
|
```kts
|
||||||
|
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Remember, placing matters! Settings appear in the order of where you add them.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Step 3.4 - Localization
|
||||||
|
|
||||||
|
Add your setting and description in the appropriate place.
|
||||||
|
|
||||||
|
Example: `src/android/app/src/main/res/values/strings.xml`
|
||||||
|
```xml
|
||||||
|
<string name="your_setting_name">Your Setting Display Name</string>
|
||||||
|
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 4 - Use Your Toggle!
|
||||||
|
|
||||||
|
Now the UI part is done find a place in the code for the toggle,
|
||||||
|
And use it to your heart's desire!
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```cpp
|
||||||
|
const bool your_value = Settings::values.your_setting_name.GetValue();
|
||||||
|
|
||||||
|
if (your_value) {
|
||||||
|
// Do something when enabled
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you wish to do something only when the toggle is disabled,
|
||||||
|
Use `if (!your_value) {` instead of `if (your_value) {`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
* Naming - Use clear, descriptive names. Something for both the devs and the users.
|
||||||
|
* Defaults - Choose safe default values (usually false for new features).
|
||||||
|
* Documentation - Write clear descriptions explaining when and why to use the setting.
|
||||||
|
* Categories - Put settings in the appropriate category.
|
||||||
|
* Order - Place related settings near each other.
|
||||||
|
* Testing - Always test on both PC and Android before committing when possible.
|
||||||
|
|
||||||
|
Thank you for reading, I hope this guide helped you making your toggle!
|
||||||
|
|
||||||
|
## Adding Debug Knobs
|
||||||
|
|
||||||
|
Debug Knobs is a 16-bit integer setting (`debug_knobs`) in the Eden Emulator that serves as a bitmask for gating various testing and debugging features. This allows developers and advanced users to enable or disable specific debug behaviors without requiring deploying of complete but temporary toggles.
|
||||||
|
|
||||||
|
The setting ranges from 0 to 65535 (0x0000 to 0xFFFF), where each bit represents a different debug feature flag.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Advantages
|
||||||
|
|
||||||
|
The main advantage is to avoid deploying new disposable toggles (those made only for testing stage, and are disposed once new feature gets good to merge). This empowers devs to be free of all frontend burocracy and hassle of new toggles.
|
||||||
|
|
||||||
|
Common advantages recap:
|
||||||
|
|
||||||
|
* **Fine-Grained Control**: Enable or disable up to 16 individual debug features independently using bit manipulation on a single build
|
||||||
|
* **Runtime Configuration**: Change debug behavior at runtime the same way as new toggles would do
|
||||||
|
* **Safe incremental development**: New debug features can be added while impact can be isolated from previous deployments
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
#### Accessing Debug Knobs (dev side)
|
||||||
|
|
||||||
|
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
//cpp side
|
||||||
|
#include "common/settings.h"
|
||||||
|
|
||||||
|
// Check if bit 0 is set
|
||||||
|
bool feature_enabled = Settings::getDebugKnobAt(0);
|
||||||
|
|
||||||
|
// Check if bit 15 is set
|
||||||
|
bool another_feature = Settings::getDebugKnobAt(15);
|
||||||
|
```
|
||||||
|
|
||||||
|
```kts
|
||||||
|
//kotlin side
|
||||||
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
|
||||||
|
// Check if bit x is set
|
||||||
|
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
|
||||||
|
```
|
||||||
|
|
||||||
|
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
||||||
|
|
||||||
|
#### Setting Debug Knobs (user side)
|
||||||
|
|
||||||
|
Developers must inform which knobs are tied to each functionality to be tested.
|
||||||
|
|
||||||
|
The debug knobs value can be set through:
|
||||||
|
|
||||||
|
1. **Desktop UI**: In the Debug configuration tab, there's a spinbox for "Debug knobs" (0-65535)
|
||||||
|
2. **Android UI**: Available as an integer setting in the Debug section
|
||||||
|
3. **Configuration Files**: Set the `debug_knobs` value in the emulator's configuration
|
||||||
|
|
||||||
|
#### Bit Manipulation Examples
|
||||||
|
|
||||||
|
To enable specific features, calculate the decimal value by setting the appropriate bits:
|
||||||
|
|
||||||
|
* **Enable only bit 0**: Value = 1 (2^0)
|
||||||
|
* **Enable only bit 1**: Value = 2 (2^1)
|
||||||
|
* **Enable bits 0 and 1**: Value = 3 (2^0 + 2^1)
|
||||||
|
* **Enable bit 15**: Value = 32768 (2^15)
|
||||||
|
|
||||||
|
### Terminology and user communication
|
||||||
|
|
||||||
|
There are two main confusions when talking about knobs:
|
||||||
|
|
||||||
|
#### Whether it's zero-based or one-based
|
||||||
|
|
||||||
|
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
||||||
|
|
||||||
|
Debug knobs are **zero-based**, which means:
|
||||||
|
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
||||||
|
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
||||||
|
|
||||||
|
#### Whether one is talking about the knob itself or about the entire parameter value (which represents all knobs)
|
||||||
|
|
||||||
|
Sometimes when an user reports: knob 3 results, it's unclear whether he's referring to knob setting with value 3 (which means both knob 0 and 1 are enabled), or to knob(3) specifically.
|
||||||
|
Whenever you're instructing tests or reporting results, be precise about whether one you're talking to avoid confusion:
|
||||||
|
|
||||||
|
#### Setting based terminology
|
||||||
|
|
||||||
|
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
||||||
|
Examples:
|
||||||
|
- **knobs=0**: no knobs enabled
|
||||||
|
- **knobs=1**: knob0 enabled, others disabled
|
||||||
|
- **knobs=2**: knob1 enabled, others disabled
|
||||||
|
- **knobs=3**: knobs 0 and 1 enabled, others disabled
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
#### Knob based terminology
|
||||||
|
|
||||||
|
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
||||||
|
Examples:
|
||||||
|
- **knob0**: knob 0 enabled, others disabled
|
||||||
|
- **knob1**: knob 1 enabled, others disabled
|
||||||
|
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
#### Example 1: Conditional Debug Logging
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void SomeFunction() {
|
||||||
|
if (Settings::getDebugKnobAt(0)) {
|
||||||
|
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
||||||
|
// Additional debug code here
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::getDebugKnobAt(1)) {
|
||||||
|
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
||||||
|
// Different debug behavior
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 2: Performance Tuning
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool UseOptimizedPath() {
|
||||||
|
// Skip optimization if debug bit 2 is set for testing
|
||||||
|
return !Settings::getDebugKnobAt(2);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 3: Feature Gating
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void ExperimentalFeature() {
|
||||||
|
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
||||||
|
|
||||||
|
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||||
|
// Fallback to stable implementation
|
||||||
|
StableImplementation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Experimental implementation
|
||||||
|
ExperimentalImplementation();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
* This setting is intended for development and testing purposes only
|
||||||
|
* Knobs must be unwired before PR creation
|
||||||
|
* The setting is per-game configurable, allowing different debug setups for different titles
|
||||||
|
|
@ -1,126 +1,70 @@
|
||||||
# Coding guidelines
|
# Coding guidelines
|
||||||
|
|
||||||
These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go.
|
These are **not** stylistic guidelines, they're, for the most part, suggestions on how to architecture new systems or improve upon the existing codebase.
|
||||||
|
|
||||||
But for new developers you may find that following these guidelines will make everything x10 easier.
|
# Foreword
|
||||||
|
|
||||||
## Naming conventions
|
Don't try to micro-optimize out of the get go, while yes, most of the code is pretty, subpar, most of these are aftertoughts and details that can be glossed over **generally**.
|
||||||
|
|
||||||
Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`.
|
Architectural issues are more important, for example an API returning a `std::string` is not as efficient as one that operates on `std::string_view` directly (cost of constructing an `std::string` w/o small-string optimization and all of that).
|
||||||
|
|
||||||
Except for Qt MOC where `functionName` is preferred.
|
Regardless of the details, try to keep things simple. As a general rule of thumb.
|
||||||
|
|
||||||
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names:
|
# C++ guidelines
|
||||||
|
|
||||||
- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `<ucontext.h>`
|
Everyone has their own way of viewing good/bad C++ practices, my general outline:
|
||||||
- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris.
|
|
||||||
- `X`: Defined by some utility headers, avoid.
|
|
||||||
- `_`: Defined by gettext, avoid.
|
|
||||||
- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants.
|
|
||||||
- `TR`: Used by some weird `<ucontext.h>` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific).
|
|
||||||
|
|
||||||
Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`.
|
- At your disposal you may use `boost::container::static_vector<>` (beware it has a ctor/initialization cost which goes up the more elements you add).
|
||||||
|
- Or you may use `boost::container::small_vector<>` (which has an initialization cost as well, and will use extra book-keeping for heap, try to keep a balance).
|
||||||
|
- Don't use `[[likely]]` or `[[unlikely]]`; PGO builds exist for that.
|
||||||
|
- Don't use inline assembly to try to outsmart the compiler unless you're 100% sure the assembly you're writing is actually good.
|
||||||
|
- And if so, try to restructure your C++ code so the compiler vectorizes it/makes it better, right?
|
||||||
|
- Or if that fails, use intrinsics instead of raw `asm volatile`.
|
||||||
|
- Use `std::optional<>` instead of `std::unique_ptr<>` if possible.
|
||||||
|
- `std::unique_ptr<>` carries indirection cost due to it being memory allocated on the heap.
|
||||||
|
- It isn't often that objects that contain `std::unique_ptr<>`, are allocated on the heap themselves, allocating even more things on the heap seems redundant.
|
||||||
|
- Avoid `std::recursive_mutex` at all costs.
|
||||||
|
- It's basically implemented as a linked list most of the time and has HEAVY performance penalties.
|
||||||
|
- Exploit the fact `std::atomic<uint32_t>/std::atomic<int32_t>` is basically free on most arches that matter.
|
||||||
|
- In x86_64, an atomic `uint32_t` is basically `mov [m32], r32`, which is essentially free/cheap.
|
||||||
|
- Avoid template parameters unless you really need them.
|
||||||
|
- For small inlineable functions this is fine, for more complex ones, please consider the generated assembly.
|
||||||
|
- Dont make your own memcpy/memset/strcpy/strncpy/etc.
|
||||||
|
- Seriously DON'T DO THIS. You will NOT beat the compiler.
|
||||||
|
- Nor 30 years of writing optimized `mem*`.
|
||||||
|
- If your code is slow, don't blame `mem*`, blame your code.
|
||||||
|
- Try to avoid using `virtual` since vtable indirection has a cost
|
||||||
|
- Avoid `dynamic_cast` and `typeid` at all costs.
|
||||||
|
- The reason is because the project has `-fno-rtti` disabled by default, due to the costs of dynamic polymorphism.
|
||||||
|
- Always copy-on-value for objects with `sizeof(void *) >= sizeof(T) * 2`, i.e objects sized as 2 pointers or less, for bigger objects you can use ref/pointer as usual.
|
||||||
|
- Try using move semantics instead of references, whenever possible.
|
||||||
|
- Remember function parameters are extremelly cheap as fuck, don't be afraid to place upto 8 parameters on a given function.
|
||||||
|
- Don't save a reference in structures of a parent object, i.e:
|
||||||
|
```c++
|
||||||
|
struct Child {
|
||||||
|
Parent& parent;
|
||||||
|
void Mehod() {
|
||||||
|
parent.Something();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
- Instead you can do the following:
|
||||||
|
```c++
|
||||||
|
struct Child {
|
||||||
|
void Mehod(Parent& parent) {
|
||||||
|
parent.Something();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
- This reduces the amount of pointers you have lying around, and also works better because of the aforementioned cheapness of parameter functions.
|
||||||
|
|
||||||
Try not using hungarian notation, if you're able.
|
# Engineering guidelines
|
||||||
|
|
||||||
## Formatting
|
Coding isn't also writing stuff but architecturing stuff, consider the following:
|
||||||
|
|
||||||
Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at.
|
- Try to reduce dependency on... dependencies
|
||||||
|
- While some dependencies are useful `boost::container` and `fmt` to name a few, remember each dependency added incurs a cost.
|
||||||
Do not put if/while/etc braces after lines:
|
- It may also be subpar with a hand rolled implementation, biggest exemplar of this is `spirv-tools` providing subpar SPIRV optimizations in comparison to the in-house optimizer.
|
||||||
|
- Try to rely less on indirection for architecturing systems
|
||||||
```c++
|
- If the underlying HLE kernel emulation requires it, try making a solution that keeps things local
|
||||||
// no dont do this
|
- For example, there isn't a need for file descriptors to each be a pointer, when they could be a fixed table size with elements that may be emplaced at will.
|
||||||
// this is more lines of code for no good reason (why braces need their separate lines?)
|
|
||||||
// and those take space in someone's screen, cumulatively
|
|
||||||
if (thing)
|
|
||||||
{ //<--
|
|
||||||
some(); // ...
|
|
||||||
} //<-- 2 lines of code for basically "opening" and "closing" an statment
|
|
||||||
|
|
||||||
// do this
|
|
||||||
if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code
|
|
||||||
// that's being closed
|
|
||||||
some(); // ...
|
|
||||||
} //<-- only one line, and it's clearer since you know its closing something [...]
|
|
||||||
|
|
||||||
// or this, albeit the extra line isn't needed (at your discretion of course)
|
|
||||||
if (thing)
|
|
||||||
some(); // ...
|
|
||||||
|
|
||||||
// this is also ok, keeps things in one line and makes it extremely clear
|
|
||||||
if (thing) some();
|
|
||||||
|
|
||||||
// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments
|
|
||||||
// in a single line, doing this will definitely ruin someone's day - just do the thing below
|
|
||||||
// vvv
|
|
||||||
if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo());
|
|
||||||
// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think
|
|
||||||
// that it makes the code more readable (the situation may be rare however)
|
|
||||||
|
|
||||||
// Wow so much clearer! Now I can actually see what each statment is meant to do!
|
|
||||||
if (thing) {
|
|
||||||
some();
|
|
||||||
thing();
|
|
||||||
a2(a1(), y1(), j1());
|
|
||||||
do_complex_shit(wa(), wo(), ploo());
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Brace rules are lax, if you can get the point across, do it:
|
|
||||||
|
|
||||||
```c++
|
|
||||||
// this is fine
|
|
||||||
do {
|
|
||||||
if (thing) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} while (other);
|
|
||||||
|
|
||||||
// this is also ok --- albeit a bit more dense
|
|
||||||
do if (thing) return 0; while (other);
|
|
||||||
|
|
||||||
// ok as well
|
|
||||||
do {
|
|
||||||
if (thing) return 0;
|
|
||||||
} while (other);
|
|
||||||
```
|
|
||||||
|
|
||||||
There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line).
|
|
||||||
|
|
||||||
```c++
|
|
||||||
// someone is going to be mad due to this
|
|
||||||
SDL_AudioSpec obtained;
|
|
||||||
device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
|
||||||
|
|
||||||
// maybe consider this
|
|
||||||
SDL_AudioSpec obtained;
|
|
||||||
if (device_name.empty()) {
|
|
||||||
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
|
|
||||||
} else {
|
|
||||||
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// or this is fine as well
|
|
||||||
SDL_AudioSpec obtained;
|
|
||||||
device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false);
|
|
||||||
```
|
|
||||||
|
|
||||||
A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least.
|
|
||||||
|
|
||||||
```c++
|
|
||||||
a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster
|
|
||||||
return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine
|
|
||||||
return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment
|
|
||||||
void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as *
|
|
||||||
```
|
|
||||||
|
|
||||||
And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally:
|
|
||||||
|
|
||||||
```c++
|
|
||||||
// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS.
|
|
||||||
template<typename T, size_t N, size_t C, size_t P> inline void what() const noexcept;
|
|
||||||
|
|
||||||
// OK use parameters like a normal person, don't be afraid to use them :)
|
|
||||||
template<typename T> inline void what(size_t n, size_t c, size_t p) const noexcept;
|
|
||||||
```
|
|
||||||
|
|
|
||||||
126
docs/policies/CodingStyle.md
Normal file
126
docs/policies/CodingStyle.md
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
# Coding Style guidelines
|
||||||
|
|
||||||
|
These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go.
|
||||||
|
|
||||||
|
But for new developers you may find that following these guidelines will make everything x10 easier.
|
||||||
|
|
||||||
|
## Naming conventions
|
||||||
|
|
||||||
|
Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`.
|
||||||
|
|
||||||
|
Except for Qt MOC where `functionName` is preferred.
|
||||||
|
|
||||||
|
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names:
|
||||||
|
|
||||||
|
- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `<ucontext.h>`
|
||||||
|
- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris.
|
||||||
|
- `X`: Defined by some utility headers, avoid.
|
||||||
|
- `_`: Defined by gettext, avoid.
|
||||||
|
- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants.
|
||||||
|
- `TR`: Used by some weird `<ucontext.h>` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific).
|
||||||
|
|
||||||
|
Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`.
|
||||||
|
|
||||||
|
Try not using hungarian notation, if you're able.
|
||||||
|
|
||||||
|
## Formatting
|
||||||
|
|
||||||
|
Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at.
|
||||||
|
|
||||||
|
Do not put if/while/etc braces after lines:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// no dont do this
|
||||||
|
// this is more lines of code for no good reason (why braces need their separate lines?)
|
||||||
|
// and those take space in someone's screen, cumulatively
|
||||||
|
if (thing)
|
||||||
|
{ //<--
|
||||||
|
some(); // ...
|
||||||
|
} //<-- 2 lines of code for basically "opening" and "closing" an statment
|
||||||
|
|
||||||
|
// do this
|
||||||
|
if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code
|
||||||
|
// that's being closed
|
||||||
|
some(); // ...
|
||||||
|
} //<-- only one line, and it's clearer since you know its closing something [...]
|
||||||
|
|
||||||
|
// or this, albeit the extra line isn't needed (at your discretion of course)
|
||||||
|
if (thing)
|
||||||
|
some(); // ...
|
||||||
|
|
||||||
|
// this is also ok, keeps things in one line and makes it extremely clear
|
||||||
|
if (thing) some();
|
||||||
|
|
||||||
|
// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments
|
||||||
|
// in a single line, doing this will definitely ruin someone's day - just do the thing below
|
||||||
|
// vvv
|
||||||
|
if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo());
|
||||||
|
// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think
|
||||||
|
// that it makes the code more readable (the situation may be rare however)
|
||||||
|
|
||||||
|
// Wow so much clearer! Now I can actually see what each statment is meant to do!
|
||||||
|
if (thing) {
|
||||||
|
some();
|
||||||
|
thing();
|
||||||
|
a2(a1(), y1(), j1());
|
||||||
|
do_complex_shit(wa(), wo(), ploo());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Brace rules are lax, if you can get the point across, do it:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// this is fine
|
||||||
|
do {
|
||||||
|
if (thing) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} while (other);
|
||||||
|
|
||||||
|
// this is also ok --- albeit a bit more dense
|
||||||
|
do if (thing) return 0; while (other);
|
||||||
|
|
||||||
|
// ok as well
|
||||||
|
do {
|
||||||
|
if (thing) return 0;
|
||||||
|
} while (other);
|
||||||
|
```
|
||||||
|
|
||||||
|
There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line).
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// someone is going to be mad due to this
|
||||||
|
SDL_AudioSpec obtained;
|
||||||
|
device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
||||||
|
|
||||||
|
// maybe consider this
|
||||||
|
SDL_AudioSpec obtained;
|
||||||
|
if (device_name.empty()) {
|
||||||
|
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
|
||||||
|
} else {
|
||||||
|
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// or this is fine as well
|
||||||
|
SDL_AudioSpec obtained;
|
||||||
|
device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false);
|
||||||
|
```
|
||||||
|
|
||||||
|
A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster
|
||||||
|
return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine
|
||||||
|
return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment
|
||||||
|
void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as *
|
||||||
|
```
|
||||||
|
|
||||||
|
And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS.
|
||||||
|
template<typename T, size_t N, size_t C, size_t P> inline void what() const noexcept;
|
||||||
|
|
||||||
|
// OK use parameters like a normal person, don't be afraid to use them :)
|
||||||
|
template<typename T> inline void what(size_t n, size_t c, size_t p) const noexcept;
|
||||||
|
```
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
# User Handbook - Adding Boolean Settings Toggles
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> This guide is intended for developers ONLY. If you are not a developer, this likely irrelevant to yourself.
|
|
||||||
>
|
|
||||||
> If you want to add temporary toggles, please refer to **[Adding Debug Knobs](AddingDebugKnobs.md)**
|
|
||||||
|
|
||||||
This guide will walk you through adding a new boolean toggle setting to Eden's configuration across both Qt's (PC) and Kotlin's (Android) UIs.
|
|
||||||
|
|
||||||
## Index
|
|
||||||
|
|
||||||
1. [Step 1 - Common Setting](#step-1-common-setting)
|
|
||||||
2. [Step 2 - Qt Toggle](#step-2-qt-toggle)
|
|
||||||
3. [Step 3 - Kotlin (Android)](#step-3-kotlin-android)
|
|
||||||
|
|
||||||
* [Step 3.1 - BooleanSetting.kt](#step-3-1-booleansetting-kt)
|
|
||||||
* [Step 3.2 - SettingsItem.kt](#step-3-2-settingsitem-kt)
|
|
||||||
* [Step 3.3 - SettingsFragmentPresenter.kt](#step-3-3-settingsfragmentpresenter-kt)
|
|
||||||
* [Step 3.4 - Localization](#step-3-4-localization)
|
|
||||||
4. [Step 4 - Use Your Toggle](#step-4-use-your-toggle)
|
|
||||||
5. [Best Practices](#best-practices)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 1 - Common Setting
|
|
||||||
|
|
||||||
Firstly add your desired toggle:
|
|
||||||
|
|
||||||
Example: `src/common/setting.h`
|
|
||||||
```cpp
|
|
||||||
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remember to add your toggle to the appropriate category, for example:
|
|
||||||
|
|
||||||
Common Categories:
|
|
||||||
|
|
||||||
* Category::Renderer
|
|
||||||
* Category::RendererAdvanced
|
|
||||||
* Category::RendererExtensions
|
|
||||||
* Category::System
|
|
||||||
* Category::Core
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> If you wish for your toggle to be `on by default` then change `false` to `true` after `linkage,`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 2 - Qt Toggle
|
|
||||||
|
|
||||||
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
|
||||||
|
|
||||||
Example: `src/qt_common/config/shared_translation.cpp`
|
|
||||||
```cpp
|
|
||||||
INSERT(Settings,
|
|
||||||
your_setting_name,
|
|
||||||
tr("Your Setting Display Name"),
|
|
||||||
tr("Detailed description of what this setting does.\n"
|
|
||||||
"You can use multiple lines.\n"
|
|
||||||
"Explain any caveats or requirements."));
|
|
||||||
```
|
|
||||||
|
|
||||||
### Make sure to:
|
|
||||||
|
|
||||||
* Keep display naming consistant
|
|
||||||
* Put detailed info in the description
|
|
||||||
* Use `\n` for line breaks in descriptions
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 3 - Kotlin (Android)
|
|
||||||
|
|
||||||
### Step 3.1 - BooleanSetting.kt
|
|
||||||
|
|
||||||
Add where it should be in the settings.
|
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
|
||||||
```kts
|
|
||||||
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
|
||||||
```
|
|
||||||
|
|
||||||
### Make sure to:
|
|
||||||
|
|
||||||
* Ensure the prefix naming matches the intended category.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 3.2 - SettingsItem.kt
|
|
||||||
|
|
||||||
Add the toggle to the Kotlin (Android) UI
|
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
|
||||||
```kts
|
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.RENDERER_YOUR_SETTING_NAME,
|
|
||||||
titleId = R.string.your_setting_name,
|
|
||||||
descriptionId = R.string.your_setting_name_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 3.3 - SettingsFragmentPresenter.kt
|
|
||||||
|
|
||||||
Add your setting within the right category.
|
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
|
||||||
```kts
|
|
||||||
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> Remember, placing matters! Settings appear in the order of where you add them.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 3.4 - Localization
|
|
||||||
|
|
||||||
Add your setting and description in the appropriate place.
|
|
||||||
|
|
||||||
Example: `src/android/app/src/main/res/values/strings.xml`
|
|
||||||
```xml
|
|
||||||
<string name="your_setting_name">Your Setting Display Name</string>
|
|
||||||
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 4 - Use Your Toggle!
|
|
||||||
|
|
||||||
Now the UI part is done find a place in the code for the toggle,
|
|
||||||
And use it to your heart's desire!
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```cpp
|
|
||||||
const bool your_value = Settings::values.your_setting_name.GetValue();
|
|
||||||
|
|
||||||
if (your_value) {
|
|
||||||
// Do something when enabled
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you wish to do something only when the toggle is disabled,
|
|
||||||
Use `if (!your_value) {` instead of `if (your_value) {`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
* Naming - Use clear, descriptive names. Something for both the devs and the users.
|
|
||||||
* Defaults - Choose safe default values (usually false for new features).
|
|
||||||
* Documentation - Write clear descriptions explaining when and why to use the setting.
|
|
||||||
* Categories - Put settings in the appropriate category.
|
|
||||||
* Order - Place related settings near each other.
|
|
||||||
* Testing - Always test on both PC and Android before committing when possible.
|
|
||||||
|
|
||||||
### Thank you for reading, I hope this guide helped you making your toggle!
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
# User Handbook - Adding Debug Knobs
|
|
||||||
|
|
||||||
Debug Knobs is a 16-bit integer setting (`debug_knobs`) in the Eden Emulator that serves as a bitmask for gating various testing and debugging features. This allows developers and advanced users to enable or disable specific debug behaviors without requiring deploying of complete but temporary toggles.
|
|
||||||
|
|
||||||
The setting ranges from 0 to 65535 (0x0000 to 0xFFFF), where each bit represents a different debug feature flag.
|
|
||||||
|
|
||||||
## Index
|
|
||||||
|
|
||||||
1. [Advantages](#advantages)
|
|
||||||
2. [Usage](#usage)
|
|
||||||
|
|
||||||
* [Accessing Debug Knobs (dev side)](#accessing-debug-knobs-dev-side)
|
|
||||||
* [Setting Debug Knobs (user side)](#setting-debug-knobs-user-side)
|
|
||||||
* [Bit Manipulation Examples](#bit-manipulation-examples)
|
|
||||||
3. [Terminology and user communication](#terminology-and-user-communication)
|
|
||||||
4. [Examples](#examples)
|
|
||||||
|
|
||||||
* [Example 1: Conditional Debug Logging](#example-1-conditional-debug-logging)
|
|
||||||
* [Example 2: Performance Tuning](#example-2-performance-tuning)
|
|
||||||
* [Example 3: Feature Gating](#example-3-feature-gating)
|
|
||||||
5. [Best Practices](#best-practices)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Advantages
|
|
||||||
|
|
||||||
The main advantage is to avoid deploying new disposable toggles (those made only for testing stage, and are disposed once new feature gets good to merge). This empowers devs to be free of all frontend burocracy and hassle of new toggles.
|
|
||||||
|
|
||||||
Common advantages recap:
|
|
||||||
|
|
||||||
* **Fine-Grained Control**: Enable or disable up to 16 individual debug features independently using bit manipulation on a single build
|
|
||||||
* **Runtime Configuration**: Change debug behavior at runtime the same way as new toggles would do
|
|
||||||
* **Safe incremental development**: New debug features can be added while impact can be isolated from previous deployments
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Accessing Debug Knobs (dev side)
|
|
||||||
|
|
||||||
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
//cpp side
|
|
||||||
#include "common/settings.h"
|
|
||||||
|
|
||||||
// Check if bit 0 is set
|
|
||||||
bool feature_enabled = Settings::getDebugKnobAt(0);
|
|
||||||
|
|
||||||
// Check if bit 15 is set
|
|
||||||
bool another_feature = Settings::getDebugKnobAt(15);
|
|
||||||
```
|
|
||||||
|
|
||||||
```kts
|
|
||||||
//kotlin side
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
|
||||||
|
|
||||||
// Check if bit x is set
|
|
||||||
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
|
|
||||||
```
|
|
||||||
|
|
||||||
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
|
||||||
|
|
||||||
### Setting Debug Knobs (user side)
|
|
||||||
|
|
||||||
Developers must inform which knobs are tied to each functionality to be tested.
|
|
||||||
|
|
||||||
The debug knobs value can be set through:
|
|
||||||
|
|
||||||
1. **Desktop UI**: In the Debug configuration tab, there's a spinbox for "Debug knobs" (0-65535)
|
|
||||||
2. **Android UI**: Available as an integer setting in the Debug section
|
|
||||||
3. **Configuration Files**: Set the `debug_knobs` value in the emulator's configuration
|
|
||||||
|
|
||||||
### Bit Manipulation Examples
|
|
||||||
|
|
||||||
To enable specific features, calculate the decimal value by setting the appropriate bits:
|
|
||||||
|
|
||||||
* **Enable only bit 0**: Value = 1 (2^0)
|
|
||||||
* **Enable only bit 1**: Value = 2 (2^1)
|
|
||||||
* **Enable bits 0 and 1**: Value = 3 (2^0 + 2^1)
|
|
||||||
* **Enable bit 15**: Value = 32768 (2^15)
|
|
||||||
|
|
||||||
## Terminology and user communication
|
|
||||||
|
|
||||||
There are two main confusions when talking about knobs:
|
|
||||||
|
|
||||||
### Whether it's zero-based or one-based
|
|
||||||
|
|
||||||
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
|
||||||
|
|
||||||
Debug knobs are **zero-based**, which means:
|
|
||||||
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
|
||||||
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
|
||||||
|
|
||||||
### Whether one is talking about the knob itself or about the entire parameter value (which represents all knobs)
|
|
||||||
|
|
||||||
Sometimes when an user reports: knob 3 results, it's unclear whether he's referring to knob setting with value 3 (which means both knob 0 and 1 are enabled), or to knob(3) specifically.
|
|
||||||
Whenever you're instructing tests or reporting results, be precise about whether one you're talking to avoid confusion:
|
|
||||||
|
|
||||||
### Setting based terminology
|
|
||||||
|
|
||||||
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
|
||||||
Examples:
|
|
||||||
- **knobs=0**: no knobs enabled
|
|
||||||
- **knobs=1**: knob0 enabled, others disabled
|
|
||||||
- **knobs=2**: knob1 enabled, others disabled
|
|
||||||
- **knobs=3**: knobs 0 and 1 enabled, others disabled
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
### Knob based terminology
|
|
||||||
|
|
||||||
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
|
||||||
Examples:
|
|
||||||
- **knob0**: knob 0 enabled, others disabled
|
|
||||||
- **knob1**: knob 1 enabled, others disabled
|
|
||||||
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Example 1: Conditional Debug Logging
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
void SomeFunction() {
|
|
||||||
if (Settings::getDebugKnobAt(0)) {
|
|
||||||
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
|
||||||
// Additional debug code here
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings::getDebugKnobAt(1)) {
|
|
||||||
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
|
||||||
// Different debug behavior
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 2: Performance Tuning
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
bool UseOptimizedPath() {
|
|
||||||
// Skip optimization if debug bit 2 is set for testing
|
|
||||||
return !Settings::getDebugKnobAt(2);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 3: Feature Gating
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
void ExperimentalFeature() {
|
|
||||||
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
|
||||||
|
|
||||||
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
|
||||||
// Fallback to stable implementation
|
|
||||||
StableImplementation();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Experimental implementation
|
|
||||||
ExperimentalImplementation();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
* This setting is intended for development and testing purposes only
|
|
||||||
* Knobs must be unwired before PR creation
|
|
||||||
* The setting is per-game configurable, allowing different debug setups for different titles
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Multiplayer
|
# User Handbook - Multiplayer
|
||||||
Use this guide to answer questions regarding and to start using the multiplayer functionality of Eden.
|
Use this guide to answer questions regarding and to start using the multiplayer functionality of Eden.
|
||||||
|
|
||||||
## Multiplayer FAQ
|
## Multiplayer FAQ
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/
|
||||||
- **[Importing Saves](./ImportingSaves.md)**
|
- **[Importing Saves](./ImportingSaves.md)**
|
||||||
- **[Installing Atmosphere Mods](./InstallingAtmosphereMods.md)**
|
- **[Installing Atmosphere Mods](./InstallingAtmosphereMods.md)**
|
||||||
- **[Installing Updates & DLCs](./InstallingUpdatesDLC.md)**
|
- **[Installing Updates & DLCs](./InstallingUpdatesDLC.md)**
|
||||||
- **[Alter Date & Time](./AlterDateTime.md)**
|
- **[Multiplayer](./Multiplayer.md)**
|
||||||
|
|
||||||
## 3rd-party Integration
|
## 3rd-party Integration
|
||||||
|
|
||||||
- **[Configuring Steam ROM Manager](./SteamROM.md)**
|
- **[Configuring Steam ROM Manager](./SteamROM.md)**
|
||||||
- **[Server hosting](ServerHosting.md)**
|
- **[Server hosting](./ServerHosting.md)**
|
||||||
- **[Syncthing Guide](./SyncthingGuide.md)**
|
- **[Syncthing Guide](./SyncthingGuide.md)**
|
||||||
- **[Third Party](./ThirdParty.md)**
|
- **[Third Party](./ThirdParty.md)**
|
||||||
- **[Obtainium](./ThirdParty.md#configuring-obtainium)**
|
- **[Obtainium](./ThirdParty.md#configuring-obtainium)**
|
||||||
|
|
@ -40,12 +40,13 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/
|
||||||
|
|
||||||
## Advanced
|
## Advanced
|
||||||
|
|
||||||
|
- **[Command Line](./CommandLine.md)**
|
||||||
- **[Custom Firmware](./CFW.md)**
|
- **[Custom Firmware](./CFW.md)**
|
||||||
|
- **[Alter Date & Time](./AlterDateTime.md)**
|
||||||
- **[How To Access Logs](./HowToAccessLogs.md)**
|
- **[How To Access Logs](./HowToAccessLogs.md)**
|
||||||
- **[Gyro Controls](./GyroControls.md)**
|
- **[Gyro Controls](./GyroControls.md)**
|
||||||
- **[Platforms and Architectures](Architectures.md)**
|
- **[Platforms and Architectures](./Architectures.md)**
|
||||||
- **[Command Line](CommandLine.md)**
|
- **[Native Application Development](./Native.md)**
|
||||||
- **[Native Application Development](Native.md)**
|
- **[Adding Boolean Settings Toggles](./AddingBooleanToggles.md)**
|
||||||
- **[Adding Boolean Settings Toggles](AddingBooleanToggles.md)**
|
|
||||||
- **[Adding Debug Knobs](./AddingDebugKnobs.md)**
|
- **[Adding Debug Knobs](./AddingDebugKnobs.md)**
|
||||||
- **[Testing](Testing.md)**
|
- **[Testing](./Testing.md)**
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ use warnings;
|
||||||
use POSIX qw(strftime);
|
use POSIX qw(strftime);
|
||||||
|
|
||||||
my $input;
|
my $input;
|
||||||
my $sampling_hz = '4000';
|
my $sampling_hz = '997';
|
||||||
my $sampling_time = '5';
|
my $sampling_time = '60';
|
||||||
my $sampling_pid = `pgrep eden`;
|
my $sampling_pid = `pgrep eden`;
|
||||||
|
chomp($sampling_pid);
|
||||||
|
|
||||||
my $sampling_program = 'eden';
|
my $sampling_program = 'eden';
|
||||||
my $sampling_type = 0;
|
my $sampling_type = 0;
|
||||||
|
|
||||||
|
|
@ -39,43 +41,27 @@ sub dtrace_ask_params {
|
||||||
|
|
||||||
sub dtrace_probe_profiling {
|
sub dtrace_probe_profiling {
|
||||||
if ($sampling_type eq 0) {
|
if ($sampling_type eq 0) {
|
||||||
|
# profile both kernel stacks and user stacks
|
||||||
return "
|
return "
|
||||||
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ {
|
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ { @[stack(100)] = count(); }
|
||||||
@[stack(100)] = count();
|
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ { @[ustack(100)] = count(); }
|
||||||
}
|
tick-".$sampling_time."s { exit(0); }";
|
||||||
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ {
|
|
||||||
@[ustack(100)] = count();
|
|
||||||
}
|
|
||||||
tick-".$sampling_time."s {
|
|
||||||
exit(0);
|
|
||||||
}";
|
|
||||||
} elsif ($sampling_type eq 1) {
|
} elsif ($sampling_type eq 1) {
|
||||||
|
# trace syscall entries
|
||||||
return "
|
return "
|
||||||
syscall:::entry /pid == ".$sampling_pid."/ {
|
syscall:::entry /pid == ".$sampling_pid."/ { \@traces[ustack(100)] = count(); }
|
||||||
\@traces[ustack(100)] = count();
|
tick-".$sampling_time."s { exit(0); }";
|
||||||
}
|
|
||||||
tick-".$sampling_time."s {
|
|
||||||
exit(0);
|
|
||||||
}";
|
|
||||||
} elsif ($sampling_type eq 2) {
|
} elsif ($sampling_type eq 2) {
|
||||||
|
# profile both kernel and user stacks with thread names attached
|
||||||
return "
|
return "
|
||||||
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ {
|
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ { @[stringof(curthread->td_name), stack(100)] = count(); }
|
||||||
@[stringof(curthread->td_name), stack(100)] = count();
|
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ { @[stringof(curthread->td_name), ustack(100)] = count(); }
|
||||||
}
|
tick-".$sampling_time."s { exit(0); }";
|
||||||
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ {
|
|
||||||
@[stringof(curthread->td_name), ustack(100)] = count();
|
|
||||||
}
|
|
||||||
tick-".$sampling_time."s {
|
|
||||||
exit(0);
|
|
||||||
}";
|
|
||||||
} elsif ($sampling_type eq 3) {
|
} elsif ($sampling_type eq 3) {
|
||||||
|
# trace I/O requests
|
||||||
return "
|
return "
|
||||||
io::start /pid == ".$sampling_pid."/ {
|
io::start /pid == ".$sampling_pid."/ { @[ustack(100)] = count(); }
|
||||||
@[ustack(100)] = count();
|
tick-".$sampling_time."s { exit(0); }";
|
||||||
}
|
|
||||||
tick-".$sampling_time."s {
|
|
||||||
exit(0);
|
|
||||||
}";
|
|
||||||
} else {
|
} else {
|
||||||
die "idk";
|
die "idk";
|
||||||
}
|
}
|
||||||
|
|
@ -111,11 +97,17 @@ sub dtrace_generate {
|
||||||
|
|
||||||
foreach my $i (0 .. $#ARGV) {
|
foreach my $i (0 .. $#ARGV) {
|
||||||
if ($ARGV[$i] eq '-h') {
|
if ($ARGV[$i] eq '-h') {
|
||||||
print "Usage: $0\n";
|
print "
|
||||||
printf "%-20s%s\n", "-p", "Prompt for parameters";
|
Usage: $0\n
|
||||||
printf "%-20s%s\n", "-g", "Generate dtrace output";
|
-p Prompt for parameters\n
|
||||||
printf "%-20s%s\n", "-s", "Continously generate output until Ctrl^C";
|
-g Generate dtrace output\n
|
||||||
printf "%-20s%s\n", "-<n>", "Select dtrace type";
|
-s Continously generate output until Ctrl^C\n
|
||||||
|
-<n> Select dtrace type\n
|
||||||
|
0: Profile kernel + user stacks (default)\n
|
||||||
|
1: Trace syscall entries\n
|
||||||
|
2: Profile kernel + user stacks with thread names\n
|
||||||
|
3: Trace I/O requests\n
|
||||||
|
";
|
||||||
} elsif ($ARGV[$i] eq '-g') {
|
} elsif ($ARGV[$i] eq '-g') {
|
||||||
dtrace_generate;
|
dtrace_generate;
|
||||||
} elsif ($ARGV[$i] eq '-s') {
|
} elsif ($ARGV[$i] eq '-s') {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue