
/***************************************************************************
 *                                                                         *
 *   KCPULoad is copyright (c) 1999-2000, Markus Gustavsson                *
 *                         (c) 2002, Ben Burton                            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kcpuproc.h"

#include <cstdlib>
#include <cstring>

// BSD-specific includes.
#ifdef Q_OS_BSD4
#include <sys/dkstat.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <string.h>
#include <kvm.h>
#ifdef Q_OS_NETBSD
#include <sys/sched.h>
#endif
#endif

/**
 * Linux
 * -----
 *
 * System information is read from /proc/stat.
 *
 * We assume /proc/stat is in one of the following formats:
 *
 *   cpu %d %d %d %d
 *   ....
 *
 *   cpu %d %d %d %d
 *   cpu0 %d %d %d %d
 *   ....
 *
 *   cpu %d %d %d %d
 *   cpu0 %d %d %d %d
 *   cpu1 %d %d %d %d
 *   ....
 *
 * where each set of four numbers is of the form:
 *
 *   user_ticks nice_ticks system_ticks idle_ticks
 *
 * BSD
 * ---
 *
 * Currently only uni-processor mode is supported for BSD.
 *
 * BSD code by Andy Fawcett <andy@athame.co.uk> and
 * Robbie Ward <linuxphreak@gmx.co.uk>, licensed under GPL2.
 *
 * Other Operating Systems
 * -----------------------
 *
 * Please, send in a patch!
 *
 * The KCPULoad maintainer is currently Ben Burton <bab@debian.org>, or
 * you could submit a patch through the KDE bug tracking system
 * (bugs.kde.org).
 */

const char* tagCPU0 = "cpu0";
const char* tagCPU1 = "cpu1";

KCPUProc::KCPUProc() {
    smp = false;
    UT = ST = NT = IT = 0;
    UT0 = ST0 = NT0 = IT0 = 0;
    UT1 = ST1 = NT1 = IT1 = 0;
    cUT = cST = cNT = cIT = 0;
    cUT0 = cST0 = cNT0 = cIT0 = 0;
    cUT1 = cST1 = cNT1 = cIT1 = 0;
    tot = tot0 = tot1 = 0;

    // Look for SMP support and take a current tick reading.

    // ========== BSD-specific (begin) ==========
#ifdef Q_OS_BSD4
    smp = false;
    readLoad();
    return;
#endif
    // ========== BSD-specific (end) ==========

    // ========== Linux-specific (begin) ==========
#ifdef Q_OS_LINUX
    if ((fd = fopen("/proc/stat", "r")) == 0)
        return;

    fscanf(fd, "%32s %d %d %d %d", tag, &cUT, &cNT, &cST, &cIT);
    fscanf(fd, "%32s %d %d %d %d", tag, &cUT0, &cNT0, &cST0, &cIT0);

    if (strcmp(tagCPU0, tag) != 0)
        return;

    fscanf(fd, "%32s %d %d %d %d", tag, &cUT1, &cNT1, &cST1, &cIT1);

    if (strcmp(tagCPU1, tag) == 0)
        smp = true;
#endif
    // ========== Linux-specific (end) ==========
}

void KCPUProc::readLoad() {
    // OS-specific local variables.

    // ========== BSD-specific (begin) ==========
#ifdef Q_OS_BSD4
    static int name2oid[2] = { 0, 3 };
    static int oidCpuTime[CTL_MAXNAME + 2];
    static size_t oidCpuTimeLen = sizeof(oidCpuTime);
    long cpuTime[CPUSTATES];
    unsigned int cpuTimeLen = sizeof(cpuTime);
    static char *name = "kern.cp_time";
    static int initialized = 0;
#endif
    // ========== BSD-specific (end) ==========

    // Prepare to take the readings.

    // ========== Linux-specific (begin) ==========
#ifdef Q_OS_LINUX
    if ((fd = fopen("/proc/stat", "r")) == 0)
        return;
#endif
    // ========== Linux-specific (end) ==========

    if(smp) {
        // The current readings must now become the previous readings.
        UT = cUT;
        ST = cST;
        NT = cNT;
        IT = cIT;

        UT0 = cUT0;
        ST0 = cST0;
        NT0 = cNT0;
        IT0 = cIT0;

        UT1 = cUT1;
        ST1 = cST1;
        NT1 = cNT1;
        IT1 = cIT1;

        // Take a fresh set of current readings (SMP mode).

        // ========== Linux-specific (begin) ==========
#ifdef Q_OS_LINUX
        fscanf(fd, "%32s %d %d %d %d", tag, &cUT, &cNT, &cST, &cIT);
        fscanf(fd, "%32s %d %d %d %d", tag, &cUT0, &cNT0, &cST0, &cIT0);
        fscanf(fd, "%32s %d %d %d %d", tag, &cUT1, &cNT1, &cST1, &cIT1);
#endif
        // ========== Linux-specific (end) ==========

        // ========== BSD-specific (begin) ==========
#ifdef Q_OS_BSD4
        // TODO: Add SMP support for BSD.
#endif
        // ========== BSD-specific (end) ==========

        // Calculate final totals.
        tot = (cUT - UT) + (cST - ST) + (cNT - NT) + (cIT - IT);
        tot0 = (cUT0 - UT0) + (cST0 - ST0) + (cNT0 - NT0) + (cIT0 - IT0);
        tot1 = (cUT1 - UT1) + (cST1 - ST1) + (cNT1 - NT1) + (cIT1 - IT1);
    } else {
        // The current readings must now become the previous readings.
        UT = cUT;
        ST = cST;
        NT = cNT;
        IT = cIT;

        // Take a fresh set of current readings (uni-processor mode).

        // ========== Linux-specific (begin) ==========
#ifdef Q_OS_LINUX
        fscanf(fd, "%32s %d %d %d %d", tag, &cUT, &cNT, &cST, &cIT);
#endif
        // ========== Linux-specific (end) ==========

        // ========== BSD-specific (begin) ==========
#ifdef Q_OS_BSD4
        if (! initialized) {
            if (sysctl(name2oid, 2, oidCpuTime, &oidCpuTimeLen, name,
                    strlen(name)) < 0)
                return;

            oidCpuTimeLen /= sizeof(int);
            initialized = 1;
        }

        if (sysctl(oidCpuTime, oidCpuTimeLen, cpuTime, &cpuTimeLen, 0, 0) < 0)
            return;

        cUT = cpuTime[CP_USER];
        cNT = cpuTime[CP_NICE];
        cST = cpuTime[CP_SYS];
        cIT = cpuTime[CP_IDLE];
#endif
        // ========== BSD-specific (end) ==========

        // Calculate final totals.
        tot = (cUT - UT) + (cST - ST) + (cNT - NT) + (cIT - IT);
    }

    // Clean up after taking readings.

    // ========== Linux-specific (begin) ==========
#ifdef Q_OS_LINUX
    fclose(fd);
#endif
    // ========== Linux-specific (end) ==========
}

