Overview
PSL1GHT provides multiple debugging methods for PS3 homebrew development. Since traditional debuggers aren’t available for PS3 homebrew, PSL1GHT offers alternative approaches including TTY output, network debugging, and remote loading.
STDOUT/STDERR TTY Redirection
By default, PSL1GHT applications automatically redirect stdout and stderr to the PS3’s low-level TTY interface (source:~/workspace/source/README.md:86-92).
TTY System Calls
PSL1GHT uses the LV2 TTY interface for console output (source:~/workspace/source/ppu/include/sys/tty.h:14-24):
#include <sys/tty.h>
// Write to TTY
s32 sysTtyWrite (s32 channel , const void * ptr , u32 len , u32 * written );
// Read from TTY
s32 sysTtyRead (s32 channel , void * ptr , u32 len , u32 * read );
Standard C functions like printf(), puts(), and fprintf(stderr, ...) automatically use TTY output. No special configuration is needed!
TTY Channels
Channel 0 : System console (requires debug firmware)
Channel 1-15 : Available for custom use
#include <sys/tty.h>
void debug_log ( const char * message ) {
u32 written;
sysTtyWrite ( 1 , message, strlen (message), & written);
}
Network Debugging with ethdebug
The most practical debugging method for retail PS3 consoles is network-based debugging using the ethdebug module.
ethdebug Module
Kammy’s ethdebug module captures TTY output and broadcasts it over UDP packets (source:~/workspace/source/README.md:86-92):
Install ethdebug
Download and install the ethdebug hook loader from the Kammy project. This provides a precompiled ethdebug module.
Enable ethdebug
Load the ethdebug module on your PS3. This is typically done through a hook loader that runs on boot.
Configure Network
Ensure your PS3 and development PC are on the same network segment for UDP broadcasts.
Receive Debug Output
Run a UDP listener on your development PC to receive the debug messages.
Receiving ethdebug Output
On your development machine:
# Linux/macOS
nc -l -u 18194
# Or use netcat to save to file
nc -l -u 18194 | tee debug.log
All printf() and fprintf(stderr, ...) output will appear in your terminal!
ethdebug broadcasts continuously, so you can start your netcat listener at any time and it will catch new output.
Manual Network Debugging
For firmware versions where ethdebug isn’t available (e.g., 3.55+), you can implement custom network debugging (source:~/workspace/source/samples/network/debugtest/source/main.c:1-64).
Network Debug Example
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <net/net.h>
#include <netinet/in.h>
static int SocketFD;
#define DEBUG_IP "192.168.1.100" // Your PC's IP
#define DEBUG_PORT 18194
void debugPrintf ( const char * fmt , ...) {
char buffer [ 0x 800 ];
va_list arg;
va_start (arg, fmt);
vsnprintf (buffer, sizeof (buffer), fmt, arg);
va_end (arg);
netSend (SocketFD, buffer, strlen (buffer), 0 );
}
void debugInit () {
struct sockaddr_in stSockAddr;
// Create UDP socket
SocketFD = netSocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
memset ( & stSockAddr, 0 , sizeof stSockAddr);
stSockAddr . sin_family = AF_INET;
stSockAddr . sin_port = htons (DEBUG_PORT);
inet_pton (AF_INET, DEBUG_IP, & stSockAddr . sin_addr );
netConnect (SocketFD, ( struct sockaddr * ) & stSockAddr,
sizeof stSockAddr);
debugPrintf ( "Network debug initialized \n " );
}
int main ( int argc , char * argv [] ) {
netInitialize ();
debugInit ();
debugPrintf ( "Application started \n " );
debugPrintf ( "argc = %d \n " , argc);
// Your application code here
return 0 ;
}
Important : Update DEBUG_IP to your development PC’s IP address. The PS3 and PC must be on the same network.
Usage Instructions
Set DEBUG_IP to your PC’s IP address in the code above
Start netcat listener on your PC:
Compile and run your application on PS3
Debug messages appear in your terminal
You need to restart netcat after each application run. Consider writing a wrapper script to auto-restart it.
Debugging with ps3load
The ps3load tool enables rapid development by loading ELF files over the network directly to your PS3 (source:~/workspace/source/ppu_rules:31).
Installing ps3load Server
On your PS3, you need a ps3load server running. This is typically provided by PS3 homebrew managers.
Using ps3load
# Build your application
make
# Load and run on PS3
ps3load myapp.elf
ps3load is much faster than creating a PKG, copying to USB, and installing. It’s ideal for rapid iteration during development.
ps3load with Arguments
# Pass command-line arguments
ps3load myapp.elf -- arg1 arg2 arg3
ps3load Features
Fast deployment : No PKG creation or USB transfer needed
Automatic execution : Loads and runs immediately
Console output : Can capture stdout/stderr
Exit codes : Reports application exit status
Debug Font Rendering
For on-screen debugging without network access, use the debug font renderer (source:~/workspace/source/samples/graphics/debugfont_renderer):
#include <debugfont.h>
#include <debugfontrenderer.h>
int main () {
// Initialize graphics
// ... GCM initialization code ...
// Initialize debug font
DbgFontInit ();
// Render debug text
DbgFontPrintf ( 0.1 f , 0.1 f , 1.0 f , 0x ffffffff ,
"FPS: %d " , current_fps);
DbgFontPrintf ( 0.1 f , 0.15 f , 1.0 f , 0x ffffffff ,
"Memory: %d KB" , memory_used);
DbgFontDraw ();
return 0 ;
}
Render text directly to the framebuffer
No external font files required
Useful for displaying real-time stats
Works without network connection
Perfect for performance counters and state information
Common Debugging Patterns
Conditional Debug Builds
// debug.h
#ifdef DEBUG_BUILD
#define DEBUG_PRINTF (...) printf ( __VA_ARGS__ )
#else
#define DEBUG_PRINTF (...) do {} while ( 0 )
#endif
// Usage
DEBUG_PRINTF ( "Value of x: %d \n " , x);
In your Makefile:
# Debug build
ifdef DEBUG
CFLAGS += -DDEBUG_BUILD -g -O0
else
CFLAGS += -O2
endif
Build with debugging:
Assert Macros
#include <assert.h>
void process_data ( void * data ) {
assert (data != NULL );
assert ( is_valid (data));
// Process data...
}
Asserts are only active in debug builds. Use assert.h carefully and ensure your release builds disable asserts or handle failures gracefully.
Memory Debugging
#define DEBUG_MALLOC ( size ) debug_malloc (size, __FILE__ , __LINE__ )
#define DEBUG_FREE ( ptr ) debug_free (ptr, __FILE__ , __LINE__ )
void * debug_malloc ( size_t size , const char * file , int line ) {
void * ptr = malloc (size);
printf ( "ALLOC: %p ( %zu bytes) at %s : %d \n " ,
ptr, size, file, line);
return ptr;
}
void debug_free ( void * ptr , const char * file , int line ) {
printf ( "FREE: %p at %s : %d \n " , ptr, file, line);
free (ptr);
}
State Dumping
void dump_game_state (GameState * state ) {
printf ( "=== Game State Dump === \n " );
printf ( "Player X: %f , Y: %f , Z: %f \n " ,
state -> player . x , state -> player . y , state -> player . z );
printf ( "Health: %d / %d \n " ,
state -> player . health , state -> player . max_health );
printf ( "Score: %d \n " , state -> score );
printf ( "Level: %d \n " , state -> current_level );
printf ( "====================== \n " );
}
Manual Timing
#include <sys/systime.h>
u64 get_time_microseconds () {
return sysGetSystemTime ();
}
void profile_function () {
u64 start = get_time_microseconds ();
// Code to profile
expensive_operation ();
u64 end = get_time_microseconds ();
printf ( "Operation took %llu microseconds \n " , end - start);
}
Frame Time Tracking
#define MAX_SAMPLES 60
u64 frame_times [MAX_SAMPLES];
int frame_index = 0 ;
void update_frame_stats () {
static u64 last_frame = 0 ;
u64 now = sysGetSystemTime ();
frame_times [frame_index] = now - last_frame;
frame_index = (frame_index + 1 ) % MAX_SAMPLES;
// Calculate average FPS
u64 total = 0 ;
for ( int i = 0 ; i < MAX_SAMPLES; i ++ ) {
total += frame_times [i];
}
u64 avg = total / MAX_SAMPLES;
float fps = 1000000.0 f / avg;
printf ( "FPS: %.2f (frame time: %llu us) \n " , fps, avg);
last_frame = now;
}
Debugging Crashes
Systematic Crash Investigation
Add Debug Output
Add printf() statements before suspected crash points: printf ( "Checkpoint 1 \n " );
risky_operation ();
printf ( "Checkpoint 2 \n " );
Narrow Down
Progressively add more checkpoints to isolate the exact line causing the crash.
Check Common Issues
Null pointer dereferences
Buffer overflows
Stack corruption
Unaligned memory access (PS3 is sensitive to this!)
Verify Memory Alignment
// PS3 requires aligned access for some types
u64 value;
memcpy ( & value , unaligned_ptr, sizeof (u64)); // Safe
// vs
u64 value = * (u64 * )unaligned_ptr; // May crash!
The PS3’s PowerPC architecture is very strict about memory alignment. Unaligned access is a common cause of crashes. See Optimization for alignment techniques.
Debugging SPU Code
SPU debugging is more challenging due to their isolated memory space:
// PPU side
printf ( "Loading SPU program... \n " );
sysSpuRawImageLoad (spu_id, & image );
printf ( "SPU loaded, starting... \n " );
// SPU side (limited printf support)
// Use mailboxes for status reporting
spu_write_out_mbox ( 0x DEADBEEF ); // Signal completion
SPU Debugging Tips
Use mailboxes to send status codes to PPU
Keep SPU programs simple during development
Test SPU code in isolation before integration
Use the sputest sample as a reference (source:~/workspace/source/samples/spu/sputest)
Debugging Checklist
See Also