/*
 * Scenario 6: USDT Probes - Custom Instrumentation
 * =================================================
 * This program demonstrates User Statically Defined Tracepoints (USDT).
 * USDT probes allow you to add custom tracing points to your code
 * that have near-zero overhead when not actively traced.
 *
 * Compile:
 *   gcc -O2 -g -o server server.c
 *
 * The probes use sys/sdt.h which is provided by systemtap-sdt-dev on Ubuntu.
 * Install: sudo apt install systemtap-sdt-dev
 *
 * EXERCISES:
 * 1. Run normally: ./server
 * 2. List probes: perf probe -x ./server --list
 * 3. Trace probes: See README.md for bpftrace examples
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>

/* 
 * USDT probe macros from systemtap-sdt-dev
 * If not available, we define dummy macros
 */
#ifdef HAVE_SDT
#include <sys/sdt.h>
#else
/* Dummy macros when SDT isn't available */
#define DTRACE_PROBE(provider, name)
#define DTRACE_PROBE1(provider, name, arg1)
#define DTRACE_PROBE2(provider, name, arg1, arg2)
#define DTRACE_PROBE3(provider, name, arg1, arg2, arg3)
#endif

/* Simulated request structure */
struct request {
    int id;
    int type;       /* 0=read, 1=write, 2=delete */
    int size;       /* payload size */
    char data[64];
};

/* Statistics */
static unsigned long total_requests = 0;
static unsigned long total_errors = 0;

/*
 * Process a single request
 * We add USDT probes at entry, exit, and error points
 */
int process_request(struct request *req) {
    /* Probe: request processing started */
    DTRACE_PROBE2(myserver, request_start, req->id, req->type);
    
    total_requests++;
    
    /* Simulate variable processing time based on request type */
    int delay_ms;
    switch (req->type) {
        case 0: delay_ms = 10 + (rand() % 20); break;   /* read: 10-30ms */
        case 1: delay_ms = 50 + (rand() % 50); break;   /* write: 50-100ms */
        case 2: delay_ms = 5 + (rand() % 10); break;    /* delete: 5-15ms */
        default: delay_ms = 100;
    }
    
    /* Simulate some work */
    usleep(delay_ms * 1000);
    
    /* Simulate occasional errors (10% chance) */
    if (rand() % 10 == 0) {
        DTRACE_PROBE2(myserver, request_error, req->id, -1);
        total_errors++;
        return -1;
    }
    
    /* Probe: request completed successfully */
    DTRACE_PROBE3(myserver, request_end, req->id, req->type, delay_ms);
    
    return 0;
}

/*
 * Batch processing function
 */
void process_batch(int batch_id, int count) {
    DTRACE_PROBE2(myserver, batch_start, batch_id, count);
    
    printf("Processing batch %d with %d requests...\n", batch_id, count);
    
    struct request req;
    for (int i = 0; i < count; i++) {
        req.id = batch_id * 1000 + i;
        req.type = rand() % 3;
        req.size = 64;
        snprintf(req.data, sizeof(req.data), "request_%d", req.id);
        
        process_request(&req);
    }
    
    DTRACE_PROBE1(myserver, batch_end, batch_id);
}

int main(int argc, char *argv[]) {
    int num_batches = 5;
    int requests_per_batch = 20;
    
    if (argc > 1) num_batches = atoi(argv[1]);
    if (argc > 2) requests_per_batch = atoi(argv[2]);
    
    srand(time(NULL));
    
    printf("USDT Probe Demo Server\n");
    printf("======================\n");
    printf("Batches: %d, Requests per batch: %d\n\n", num_batches, requests_per_batch);
    
#ifndef HAVE_SDT
    printf("Note: Compiled without USDT support.\n");
    printf("To enable probes: sudo apt install systemtap-sdt-dev\n");
    printf("Then compile with: gcc -DHAVE_SDT -O2 -g -o server server.c\n\n");
#endif
    
    DTRACE_PROBE(myserver, server_start);
    
    for (int i = 0; i < num_batches; i++) {
        process_batch(i, requests_per_batch);
    }
    
    DTRACE_PROBE(myserver, server_stop);
    
    printf("\n=== Summary ===\n");
    printf("Total requests: %lu\n", total_requests);
    printf("Total errors:   %lu (%.1f%%)\n", 
           total_errors, 100.0 * total_errors / total_requests);
    
    return 0;
}
