A tracing profiler for the Sega MegaDrive/Genesis

Overview

md-profiler, a tracing profiler for the Sega MegaDrive/Genesis

This program, meant to be used with this fork of BlastEm, helps you finding bottlenecks and having a better understanding of the performance of your games and ROM hacks. The currently supported assemblers, compilers and toolchains are asm68k, as, and gcc/SGDK.

Screenshot

Basic usage

Installation

Download md-profiler and this modified version of BlastEm, only Windows binaries are provided, other OS will have to compile from the source code.

Generating symbols

While not strictly required, symbols will allow you to make sense of the output of this program as they will allow you to see your labels and function names rather than raw addresses, you probably want to use symbols, it's pretty easy but the instructions differ slightly depending of what you use to build your game.

Asm68k

When you build your game, the command should looks like this:

asm68k <OPTIONS...> mygame.asm, mygame.bin, mygame.sym, mygame.lst

The important part is mygame.sym, this is the symbol file that will be generated. you may also want to add the /o v+ switch to your options, this will tell asm68k to also list private labels in the symbol file (by default, only global labels are exported), optionally, you can also add the /o c+ switch, this will treat the labels as case sensitive, by default they will be exported as lowercase unless you do this.

AS

Add -g MAP to your build command, the .map file generated is your symbol file

SGDK

SGDK default build scripts already generate symbols.txt which is your symbol file

Recording a trace

Launch BlastEm with your game, when you want to record a trace, hit the 'u' key, this will open the BlastEm debugger console. Enter mdp <output.mdp>
This will resume your game, and generate the mdp file for profiling, when you are done, press 'u' again, and enter smdp in the console to stop the trace recording.

Generating the json trace

It's now time to use this program, the command is:

md-profiler -s <SYMBOLS> -i <INPUT> -o <OUTPUT>

SYMBOLS is your symbol file, INPUT is the mdp file and OUTPUT is the json file this program will generate.

Viewing the trace

You have several options:
You can use https://ui.perfetto.dev/ in any browser, with the Open trace button in the top left, select your json file
Or can use Google Chrome's chrome://tracing/ interface, press the Load button, on the top left and select your json file

Limitations and working around them

  • By default, the profiler only follows explicit subroutine calls with JSR or BSR instructions, if you jump to, or fall trough subroutine code, it won't show that subroutine as being currently called. This is fixable however, even without changing your code, but it will require a bit of manual input on your part, see the Advanced usage section for more details.
  • C code with optimizations turned on tends to aggressively inline a vast amount of functions, and thus they don't appear in the graph. You can change the compiler options to make it inline less but keep in mind that builds with less inlining will not perform as well, and you may not get measurements that represent accurately how your optimized (with inlining) builds perform. The Advanced usage section contains a workaround that lets you profile inlined functions without affecting the generated code, at the cost of having to insert annotations manually in your source files.

Advanced usage: Manual intervals

Writing your interval files

On top of automatically tracing subroutine calls and interrupts, you can also manually observe how long your code spends between two (or more) arbitrary points, you can specify these points by creating a text file where each line specify an interval and has this format:

ENTRY POINTS,EXIT POINTS,OPTIONAL NAME, OPTIONAL CATEGORY

Entry points and exit points can be labels or hex-formatted addresses, you can specify multiple entry points and/or multiple exit points by separating each with a semicolon ';', for instance:

MySubroutineEntry1;MySubroutineEntry2;MySubroutineEntry3,MySubroutineExit1;MySubroutineExit2,MySubroutine

An interval will start when any of the entry point is reached, and will end when any of the exit point is reached. If a label is both an entry point and and exit point for the same interval, it will stop the interval (if it was already started) and immediately start a new one.

If you don't specify a category, the interval will be stacked with others, automatically traced subroutines in the main thread. In case this is not what you want, you can name specify another, separate category to put that interval in, for instance:

V_Int, WaitForVint, FrameTime, Frame time

Will create the category "Frame time" and put it below the two default categories "Main thread" and "Interrupts"

Passing the intervals to BlastEm

Now you need to use md-profiler in a special mode, which will generate a file to tell BlastEm which addresses it should pay attention to:

md-profiler -m <INTERVALS> -s <SYMBOLS> -b <BREAKPOINTS OUTPUT FILE>

In BlastEm use the mbp command to specify the breakpoint file location before recording the trace file with the mdp command:

mbp <BREAKPOINTS OUTPUT FILE>
mdp <output.mdp>

And then you can use the mdp command to record the trace as usual, except you also specify the interval file:

md-profiler -m <INTERVALS> -s <SYMBOLS> -i <INPUT> -o <OUTPUT>

Profiling inlined C functions

In your C code, you can use these macros

#define LABEL(name) asm volatile("mdp_label_" name "_%=: .global mdp_label_" name "_%=":);
#define FUNCTION_START(name) LABEL(name "_start");
#define FUNCTION_END(name)   LABEL(name "_end");

to define global labels and function start/end markers that the profiler can use

For instance, if myfunction gets inlined when compiling but you still want to profile it, you can do this:

s16 myfunction(s16 a) {
    FUNCTION_START("myfunction");
    if (a > 0) {
        FUNCTION_END("myfunction");
        return a+1;
    }
    if (a < 0) {
        FUNCTION_END("myfunction");
        return a-1;
    }
    FUNCTION_END("myfunction");
    return 0;
}

You need to add FUNCTION_START at the start of the function and the FUNCTION_END before each return statement (including the implicit one at the end of the function for void functions)
In your interval file, you just specify the function name alone on a line, like so:

myfunction

I know that ideally gcc would be able to automate this work automatically, I am aware of -finstrument-functions, but this isn't really what I want, I would need something like -finstrument-macros
Anyway, if you are aware of a better way of doing this, please let me know.

You might also like...
A tracing layer for macOS/iOS's `oslog`

tracing_oslog This is a tracing layer for the Apple OS logging framework. Activities are used to handle spans, Example use tracing_oslog::OsLogger; l

Emit ETW events in tracing-enabled Rust applications.
Emit ETW events in tracing-enabled Rust applications.

tracing-etw Emit ETW events in tracing-enabled Rust applications. This crate depends on rust_win_etw. There are four ETW events. fn NewSpan(span_id: u

A patch to fix tracing LocalTime problem.

tracing-local-time A patch to fix tracing LocalTime problem. Tracing-subscriber now has a bug in LocalTime, so build ourselves' to fix it. In this pat

An example of a fairing for rocket to use tracing (as this pops up at many places in dicussions and questions)
An example of a fairing for rocket to use tracing (as this pops up at many places in dicussions and questions)

Rocket Tracing Fairing Example This repository aims to give a short example of how you can add a Fairing to your Rocket for tracing and how to use it

A dynamic binary tracing tool

Backlight Backlight is a dynamic binary tracing tool. Install $ git clone [email protected]:JoshMcguigan/backlight.git $ cd backlight $ cargo install-b

Tracing layer to quickly inspect spans and events

tracing-texray First, a word of warning: This is alpha software. Don't run this in prod or anywhere where a panic would ruin your day. tracing-texray

tracing - a framework for instrumenting Rust programs to collect structured, event-based diagnostic information

tracing-appender Writers for logging events and spans Documentation | Chat Overview tracing is a framework for instrumenting Rust programs to collect

Middlewares and tools to integrate axum + tracing + opentelemetry

axum-tracing-opentelemetry Middlewares and tools to integrate axum + tracing + opentelemetry. Read OpenTelemetry header from incoming request Start a

A rust `tracing` compatible framework inspired by log4rs.

trace4rs This crate allows users to configure output from tracing in the same way as you would configure the output of log4rs. Overview For a usage ex

Releases(1.1.0)
Owner
null
Malloc frequency profiler

Malloc frequency profiler This malloc frequency profiler helps detect program hotspots that perform a large number of memory allocations.

Leonid Ryzhyk 7 Jan 7, 2022
Simple timings profiler

profl Simple timings profiler Example fn main() -> std::io::Result<()> { profl::init("timings.data"); let mut total = 0; for i in 0..1000

Broxus 1 Dec 9, 2021
Py-spy - Sampling profiler for Python programs

py-spy: Sampling profiler for Python programs py-spy is a sampling profiler for Python programs. It lets you visualize what your Python program is spe

Ben Frederickson 9.5k Jan 8, 2023
🐦 Friendly little instrumentation profiler for Rust 🦀

?? puffin The friendly little instrumentation profiler for Rust How to use fn my_function() { puffin::profile_function!(); ... if ... {

Embark 848 Dec 29, 2022
The axiom profiler for exploring and visualizing SMT solver quantifier instantiations (made via E-matching).

Axiom Profiler A tool for visualising, analysing and understanding quantifier instantiations made via E-matching in a run of an SMT solver (at present

Viper Project 18 Oct 18, 2022
A memory profiler for Linux.

Bytehound - a memory profiler for Linux Features Can be used to analyze memory leaks, see where exactly the memory is being consumed, identify tempora

Koute 3.3k Dec 25, 2022
Sampling profiler and tracer for Ruby (CRuby) which runs in BPF

rbperf rbperf is a low-overhead sampling profiler and tracer for Ruby (CRuby) which runs in BPF Build To build rbperf you would need a Linux machine w

Javier Honduvilla Coto 75 Dec 19, 2022
🐝🦀🔥 An ebpf based CPU profiler written in Rust

profile-bee ?? ?? ?? Profile Bee is an eBPF based CPU profiler written in Rust for performance and efficiency. Aya is used for building the BPF progra

Joshua Koo 5 Dec 16, 2022
Error propagation tracing in Rust.

Propagate Error propagation tracing in Rust. Why Propagate? Being able to trace the cause of an error is critical for many types of software written i

Ben Reeves 10 Sep 23, 2021
This crate bridges between gstreamer and tracing ecosystems.

This crate provides a bridge between gstreamer and the tracing ecosystem. The goal is to allow Rust applications utilizing GStreamer to better integra

Standard Cognition OSS 17 Jun 7, 2022