/* EXERCISE 05 — BROKEN ON PURPOSE. Do not copy this shape into real code.
 *
 * A crash-log scanner. It walks a log file line by line and tallies the lines
 * that contain a marker. The author needed to split on newlines and could not
 * find a "read a line" system call, so they wrote one: read a byte, test it,
 * repeat.
 *
 * Symptom as reported by the field: "scanning a 64 MB log takes twenty
 * seconds. The disk is idle the whole time and we are not even at 100% of one
 * core. Is the SSD broken?"
 *
 * Build and run:
 *   clang -O2 -g -Wall -Wextra logscan.c -o /tmp/logscan_broken
 *   /tmp/logscan_broken
 *
 * The answer this program produces is CORRECT. Nothing here is a race and
 * nothing here is a leak.
 *
 * Every measurement is printed as key=value so a script can assert on it.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/stat.h>

#define BLOCKS      96          /* log is built in 1024-line blocks      */
#define LINES       (BLOCKS * 1024)  /* lines in the generated log       */
#define LINE_BYTES  96          /* bytes per line, including the newline */
#define WATCHDOG_S  300

static double now_ms(void) {
    struct timeval t; gettimeofday(&t, NULL);
    return t.tv_sec * 1000.0 + t.tv_usec / 1000.0;
}

static void *watchdog(void *unused) {
    (void)unused;
    sleep(WATCHDOG_S);
    const char m[] = "WATCHDOG: no progress within budget - forcing _exit(75).\n";
    ssize_t ignored = write(2, m, sizeof m - 1); (void)ignored;
    _exit(75);
}

/* Build the log in a temporary file we own and delete. */
static int make_log(char *path, size_t pathsz, size_t *bytes_out) {
    snprintf(path, pathsz, "%s/logscan.%d.log",
             getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp", (int)getpid());
    int fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
    if (fd < 0) { perror("open"); return -1; }
    char *chunk = malloc((size_t)LINE_BYTES * 1024);
    size_t total = 0;
    for (int block = 0; block < BLOCKS; block++) {
        char *p = chunk;
        for (int i = 0; i < 1024; i++) {
            int seq = block * 1024 + i;
            memset(p, 'a' + (seq % 26), LINE_BYTES - 1);
            if (seq % 97 == 0) memcpy(p + 8, "MARKER", 6);
            p[LINE_BYTES - 1] = '\n';
            p += LINE_BYTES;
        }
        ssize_t w = write(fd, chunk, (size_t)LINE_BYTES * 1024);
        if (w < 0) { perror("write"); free(chunk); close(fd); return -1; }
        total += (size_t)w;
    }
    free(chunk);
    fcntl(fd, F_FULLFSYNC, 0);
    close(fd);
    *bytes_out = total;
    return 0;
}

int main(void) {
    pthread_t wd;
    pthread_create(&wd, NULL, watchdog, NULL);
    pthread_detach(wd);

    char path[512]; size_t filebytes = 0;
    if (make_log(path, sizeof path, &filebytes) != 0) return 1;

    int fd = open(path, O_RDONLY);
    if (fd < 0) { perror("open"); return 1; }

    char line[512];
    size_t len = 0;
    long lines = 0, markers = 0, syscalls = 0;
    unsigned long long checksum = 0;

    double t0 = now_ms();
    for (;;) {
        char c;
        /* One system call per byte. Each one is a full user-to-kernel
         * transition to copy a single character that the kernel already had
         * sitting in the page cache. */
        ssize_t n = read(fd, &c, 1);
        syscalls++;
        if (n <= 0) break;
        if (c == '\n') {
            line[len < sizeof line - 1 ? len : sizeof line - 1] = '\0';
            lines++;
            if (strstr(line, "MARKER")) markers++;
            checksum += (unsigned long long)len;
            len = 0;
        } else if (len < sizeof line - 1) {
            line[len++] = c;
        }
    }
    double ms = now_ms() - t0;
    close(fd);
    unlink(path);

    printf("fileBytes=%zu\n", filebytes);
    printf("lines=%ld\n", lines);
    printf("markers=%ld\n", markers);
    printf("checksum=%llu\n", checksum);
    printf("readSyscalls=%ld\n", syscalls);
    printf("bytesPerSyscall=%.1f\n", syscalls ? (double)filebytes / (double)syscalls : 0.0);
    printf("elapsedMs=%.1f\n", ms);
    printf("throughputMBps=%.1f\n", (filebytes / 1048576.0) / (ms / 1000.0));
    return lines == LINES ? 0 : 1;
}
