root/src/bin/ps.c
/*
 * Copyright 2002-2008, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Francois Revol (mmu_man)
 *              Salvatore Benedetto <salvatore.benedetto@gmail.com>
 *              Bjoern Herzig (xRaich[o]2x)
 *              Thomas Schmidt <thomas.compix@googlemail.com>
 */
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#include <OS.h>


enum {
        Team = 0,
        Id,
        Threads,
        Gid,
        Uid
};

struct ColumnIndo {
        const char* name;
        const char* header;
        const char* format;
} Infos[] = {
        { "Team",               "%-50s",        "%-50s"  },
        { "Id",                 "%5s",          "%5" B_PRId32  },
        { "Threads",    "#%7s",         "%8" B_PRId32 },
        { "Gid",                "%4s",          "%4d" },
        { "Uid",                "%4s",          "%4d" }
};

#define maxColumns  10
int Columns[maxColumns] = { Team, Id, Threads, Gid, Uid, 0 };
int ColumnsCount = 5;

const char* sStates[] = {"run", "rdy", "msg", "zzz", "sus", "wait"};

static void printTeamThreads(team_info* teamInfo, bool printSemaphoreInfo);
static void printTeamInfo(team_info* teamInfo, bool printHeader);


static void
printTeamInfo(team_info* teamInfo, bool printHeader)
{
        int i = 0;
        if (printHeader) {
                for (i = 0; i < ColumnsCount; i++) {
                        printf(Infos[Columns[i]].header, Infos[Columns[i]].name);
                        putchar(' ');
                }
                puts("");
        }


        for (i = 0; i < ColumnsCount; i++) {
                switch (Columns[i]) {
                        case Team:
                                printf(Infos[Team].format, teamInfo->args);
                                break;
                        case Id:
                                printf(Infos[Id].format, teamInfo->team);
                                break;
                        case Threads:
                                printf(Infos[Threads].format, teamInfo->thread_count);
                                break;
                        case Gid:
                                printf(Infos[Gid].format, teamInfo->gid);
                                break;
                        case Uid:
                                printf(Infos[Uid].format, teamInfo->uid);
                                break;
                }
                putchar(' ');
        }
        puts("");
}


static void
printTeamThreads(team_info* teamInfo, bool printSemaphoreInfo)
{
        const char* threadState;
        int32 threadCookie = 0;
        sem_info semaphoreInfo;
        thread_info threadInfo;

        // Print all info about its threads too
        while (get_next_thread_info(teamInfo->team, &threadCookie, &threadInfo)
                >= B_OK) {
                if (threadInfo.state < B_THREAD_RUNNING
                        || threadInfo.state > B_THREAD_WAITING)
                        // This should never happen
                        threadState = "???";
                else
                        threadState = sStates[threadInfo.state - 1];

                printf("%-37s %5" B_PRId32 " %8s %4" B_PRId32 " %8" B_PRIu64 " %8"
                        B_PRId64 " ", threadInfo.name, threadInfo.thread, threadState,
                        threadInfo.priority, (threadInfo.user_time / 1000),
                        (threadInfo.kernel_time / 1000));

                if (printSemaphoreInfo) {
                        if (threadInfo.state == B_THREAD_WAITING && threadInfo.sem != -1) {
                                status_t status = get_sem_info(threadInfo.sem, &semaphoreInfo);
                                if (status == B_OK) {
                                        printf("%s(%" B_PRId32 ")\n", semaphoreInfo.name,
                                                semaphoreInfo.sem);
                                } else {
                                        printf("%s(%" B_PRId32 ")\n", strerror(status),
                                                threadInfo.sem);
                                }
                        } else
                                puts("");
                } else
                        puts("");
        }
}


int
main(int argc, char** argv)
{
        team_info teamInfo;
        int32 teamCookie = 0;
        system_info systemInfo;
        bool printSystemInfo = false;
        bool printThreads = false;
        bool printHeader = true;
        bool printSemaphoreInfo = false;
        bool customizeColumns = false;
        // match this in team name
        char* string_to_match;

        int c;

        while ((c = getopt(argc, argv, "-ihaso:")) != EOF) {
                switch (c) {
                        case 'i':
                                printSystemInfo = true;
                                break;
                        case 'h':
                                printf( "usage: ps [-hais] [-o columns list] [team]\n"
                                                "-h : show help\n"
                                                "-i : show system info\n"
                                                "-s : show semaphore info\n"
                                                "-o : display team info associated with the list\n"
                                                "-a : show threads too (by default only teams are "
                                                        "displayed)\n");
                                return 0;
                                break;
                        case 'a':
                                printThreads = true;
                                break;
                        case 's':
                                printSemaphoreInfo = true;
                                break;
                        case 'o':
                                if (!customizeColumns)
                                        ColumnsCount = 0;
                                customizeColumns = true;
                                /* fallthrough */
                        case 1:
                        {
                                size_t i = 0;
                                if (c == 1 && !customizeColumns)
                                        break;
                                for (i = 0; i < sizeof(Infos) / sizeof(Infos[0]); i++)
                                        if (strcmp(optarg, Infos[i].name) == 0
                                                        && ColumnsCount < maxColumns) {
                                                Columns[ColumnsCount++] = i;
                                                continue;
                                        }
                                break;
                        }
                }
        }

        // TODO: parse command line
        // Possible command line options:
        //      -t  pstree like output

        if (argc == 2 && (printSystemInfo || printThreads))
                string_to_match = NULL;
        else
                string_to_match = (argc >= 2 && !customizeColumns)
                        ? argv[argc - 1] : NULL;

        if (!string_to_match) {
                while (get_next_team_info(&teamCookie, &teamInfo) >= B_OK) {
                        printTeamInfo(&teamInfo, printHeader);
                        printHeader = false;
                        if (printThreads) {
                                printf("\n%-37s %5s %8s %4s %8s %8s\n", "Thread", "Id", \
                                        "State", "Prio", "UTime", "KTime");
                                printTeamThreads(&teamInfo, printSemaphoreInfo);
                                printf("----------------------------------------------" \
                                        "-----------------------------\n");
                                printHeader = true;
                        }
                }
        } else {
                while (get_next_team_info(&teamCookie, &teamInfo) >= B_OK) {
                        char* p = teamInfo.args;
                        if ((p = strchr(p, ' ')))
                                *p = '\0'; /* remove arguments, keep only argv[0] */
                        p = strrchr(teamInfo.args, '/'); /* forget the path */
                        if (p == NULL)
                                p = teamInfo.args;
                        if (strstr(p, string_to_match) == NULL)
                                continue;
                        printTeamInfo(&teamInfo, true);
                        printf("\n%-37s %5s %8s %4s %8s %8s\n", "Thread", "Id", "State", \
                                "Prio", "UTime", "KTime");
                        printTeamThreads(&teamInfo, printSemaphoreInfo);
                }
        }

        if (printSystemInfo) {
                // system stats
                get_system_info(&systemInfo);
                printf("\nSystem Info\n");
                printf("%" B_PRIu64 "k (%" B_PRIu64 " bytes) total memory\n",
                        (systemInfo.max_pages * B_PAGE_SIZE / 1024),
                        (systemInfo.max_pages * B_PAGE_SIZE));
                printf("%" B_PRIu64 "k (%" B_PRIu64 " bytes) currently committed\n",
                        (systemInfo.used_pages * B_PAGE_SIZE / 1024),
                        (systemInfo.used_pages * B_PAGE_SIZE));
                printf("%" B_PRIu64 "k (%" B_PRIu64 " bytes) currently available\n",
                        (systemInfo.max_pages - systemInfo.used_pages) * B_PAGE_SIZE / 1024,
                        (systemInfo.max_pages - systemInfo.used_pages) * B_PAGE_SIZE);
                printf("%2.1f%% memory utilisation\n",
                        (float)100 * systemInfo.used_pages / systemInfo.max_pages);
        }
        return 0;
}