spu-tools-2.3.0.136/0000755000202400020240000000000011142323136013347 5ustar arthurarthurspu-tools-2.3.0.136/spu-tools.spec0000644000202400020240000000415611117752645016213 0ustar arthurarthurSummary: user space tools for Cell/B.E. Name: spu-tools Version: 1.1 Release: 6 License: GPL Group: Applications/System Source0: spu-tools.tar.bz2 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root Requires: ncurses BuildRequires: help2man %define _prefix /usr %description The spu-tools package contains user space tools for Cell/B.E. Currently, it contain two tools: - spu-top: a tool like top to watch the SPU's on a Cell BE System. It shows information about SPUs and running SPU contexts. - spu-ps: a tool like ps, which dumps a report on the currently running SPU contexts. %prep %setup -c -q %build cd src/ make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT cd src/ make DESTDIR=$RPM_BUILD_ROOT install %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %dir /%{_prefix}/bin/spu-top %dir /%{_prefix}/bin/spu-ps %dir /%{_prefix}/share/man/man1/spu-top.1.gz %dir /%{_prefix}/share/man/man1/spu-ps.1.gz %changelog * Tue Sep 02 2008 Andre Detsch 1.1-6 - Enhanced PID retrieval. * Fri Jul 04 2008 Andre Detsch 1.1-5 - Support for spu threads with arbitrary spufs entry names. * Wed May 28 2008 Andre Detsch 1.1-4 - Added TID field to SPU view of spu-top. * Fri Apr 11 2008 Andre Detsch 1.1-3 - Fix escape sequences handling on spu-top. - Fixed help example for spu-ps. * Tue Mar 18 2008 Andre Detsch 1.1-2 - Fixed load averange printing. * Thu Mar 13 2008 Andre Detsch 1.1-1 - Making Per-Process view explanation a bit clearer. - Additional space for some spu statistics. * Thu Jun 14 2007 Andre Detsch 1.0-2 - Setting BuildRoot at spec file. - General fixes (src/ChangeLog) * Tue May 29 2007 Andre Detsch 1.0 - Package rename: spu-top to spu-tools. - Complete rewrite for Cell SDK 3.0. * Tue Apr 03 2007 Markus Deuling 0.2 - Seperate Process info and IRQ stats. - Use ncurses library. - Retrieve number of CPUs in the system. - Remove valid flag. * Tue Mar 13 2007 Markus Deuling 0.1 - Initial version. spu-tools-2.3.0.136/src/0000755000202400020240000000000011142323136014136 5ustar arthurarthurspu-tools-2.3.0.136/src/spu-tools.h0000644000202400020240000001217011037572737016275 0ustar arthurarthur/* * Copyright (C) 2006 IBM Corp. * * Author: Andre Detsch * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef SPUPS_H #define SPUPS_H #include #ifndef SPUFS_PATH #define SPUFS_PATH "/spu" #endif #ifndef PROCFS_PATH #define PROCFS_PATH "/proc" #endif #ifndef SYSFS_PATH #define SYSFS_PATH "/sys" #endif typedef unsigned long long u64; typedef unsigned int u32; typedef char s8; struct field { char id; const char* name; const char* name_format; const char *format; const char *description; int do_show; const char *id_string; }; enum time { TIME_USER=0, TIME_NICE, TIME_SYSTEM, TIME_IOWAIT, TIME_LOADED, TIME_TOTAL, TIME_IDLE, TIME_MAX }; #define for_each_time(time) for((time)=0; time Copyright (C) 19yy 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. spu-tools-2.3.0.136/src/spu-ps.c0000644000202400020240000001155711010576673015554 0ustar arthurarthur/* * Copyright (C) 2007 IBM Corp. * * Author: Andre Detsch * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "spu-tools.h" #include #include #include #include #include #include #include #include static void print_header(struct field *fields) { int i = 0; while (fields[i].id) { if (fields[i].do_show) printf(fields[i].name_format, fields[i].name); i++; } printf("\n"); } #define MAX_LINE_SIZE 1024 static void dump_ctxs_or_spus(void **ctxs_or_spus, struct field *fields) { int i = 0; char buf[MAX_LINE_SIZE]; if (!ctxs_or_spus) return; while (ctxs_or_spus[i]) { int j; int chars = 0; for (j = 0; fields[j].id; j++) { if (!fields[j].do_show) continue; chars += print_ctx_field((struct ctx *)ctxs_or_spus[i], buf+chars, fields[j].id, fields[j].format); } printf(buf); printf("\n"); i++; } return; } #define DUPLICATE_FIELD -1 #define INVALID_FIELD -2 static int set_field_order(const char* field_id_string, int index) { int i; for (i = 0; ctx_fields[i].id; i++) { if (ctx_fields[i].id_string && !strcmp(ctx_fields[i].id_string, field_id_string)) { if (i < index) return DUPLICATE_FIELD; struct field tmp; tmp = ctx_fields[index]; ctx_fields[index] = ctx_fields[i]; ctx_fields[i] = tmp; ctx_fields[index].do_show = 1; return 0; } } return INVALID_FIELD; } static void hide_fields(int index) { int i; for (i = index; ctx_fields[i].id; i++) ctx_fields[i].do_show = 0; } static int set_sort_order(const char *id_string) { int i; for (i = 0; ctx_fields[i].id; i++) { if (ctx_fields[i].id_string && !strcmp(ctx_fields[i].id_string, id_string)) { set_ctx_sort_field(ctx_fields[i].id); return 0; } } return INVALID_FIELD; } #include #define die(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while (0) static void dump_field_names() { int i; for (i = 0; ctx_fields[i].id; i++) if (ctx_fields[i].id_string) printf(" %-25s - %s\n", ctx_fields[i].id_string, ctx_fields[i].description); } static void dump_default_fields() { int first = 1; int i; for (i = 0; ctx_fields[i].id; i++) { if (ctx_fields[i].id_string) { printf("%s%s", first?" ":",", ctx_fields[i].id_string); first = 0; } } printf("\n"); } static void version() { printf( "spu-ps (spu-tools 1.0)\n\n" "Copyright (C) IBM 2007.\n" "Released under the GNU GPL.\n\n" ); } static void usage() { printf( "Usage: spu-ps [OPTIONS]\n" "Dump information about the running spu contexts.\n\n" "Options:\n" " -f, --fields=FIELD,FIELD,... list of fields to be dumped\n" " -s, --sort=FIELD sort the output according to the given field\n" " -h, --help display this help and exit\n" " -v, --version output version information and exit\n\n" "Valid field names are:\n" ); dump_field_names(); printf("\nDefault shown fields are:\n"); dump_default_fields(); printf("\nExamples:\n"); printf(" spu-ps\n\n"); printf(" spu-ps -f PPU_PID,USER_NAME -s USER_NAME\n\n"); } int main(int argc, char **argv) { int c; int i, ret; static struct option long_options[] = { {"sort-by", 1, 0, 's'}, {"fields", 1, 0, 'f'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {0, 0, 0, 0} }; for (i = 0; ctx_fields[i].id; i++) { if (!ctx_fields[i].id_string) ctx_fields[i].do_show = 0; } while (1) { c = getopt_long(argc, argv, "f:s:hv", long_options, NULL); if (c == -1) break; char *field; switch(c) { case 's': ret = set_sort_order(optarg); if (ret == INVALID_FIELD) die("Invalid field name %s.\n", optarg); break; case 'f': i = 0; while ((field = strsep(&optarg, ",")) != NULL) { ret = set_field_order(field, i++); if (ret == DUPLICATE_FIELD) die("Field list must contain only one occurence of each field" " (%s was listed more than once)\n", field); else if (ret == INVALID_FIELD) die("Invalid field name %s.\n", field); } hide_fields(i); break; case 'v': version(); exit(0); default: usage(); exit(0); } } print_header(ctx_fields); dump_ctxs_or_spus((void**)get_spu_contexts(1), ctx_fields); return 0; } spu-tools-2.3.0.136/src/ChangeLog0000644000202400020240000000337711117752645015737 0ustar arthurarthur2008-12-09 Andre Detsch * ctx-info.c: Handle chdir return code, to avoid trying to access tid, phys-id and stat files in the wrong location. 2008-09-02 Andre Detsch * ctx-info.c: Enhanced PID retrieval. 2008-07-04 Andre Detsch * ctx-info.c: Support for spu threads with arbitrary spufs entry names. 2008-05-28 Andre Detsch * spu-top: Added TID field to SPU view. 2008-04-11 Andre Detsch * spu-top.c: Fix escape sequences handling. * spu-ps.c: Fixed help example. 2008-03-18 Andre Detsch * spu-top.c: Fixed load averange printing. 2008-03-13 Andre Detsch * spu-top.c: making Per-Process view explanation a bit clearer. * spu-info.c: additional space for some statistics. 2007-07-11 Andre Detsch * ctx-info.c: do not adding loaded time to ctx total time field. 2007-06-26 Andre Detsch * proc-info.c: giving %SPU field one more screen column. * spu-ps.c, ctx-info.c: disabling %SPU field on spu-ps. 2007-06-14 Andre Detsch * ctx-info.c, spu-info.c: added missing word in CTX_PPE_LIBRARY and SPU_PPE_LIBRARY fields textual description. * spu-ps.c: fix on --fields= option handling. * spu-top.c: fixed handling of 'D'/'A' keys (change sort order to descending/ascending). * proc-info.c: removed potential memory leak. * spu-top.c: do not printing garbage when all fields are hidden. 2007-06-05 Markus Deuling * spu-info.c (spu_update): Remove error msg after fopen. * ChangeLog: New file. * Makefile: Use ./spu-top instead of spu-top for help2man to prevent conflicts with installed versions of spu-{top,ps}. spu-tools-2.3.0.136/src/Makefile0000644000202400020240000000177510703676340015622 0ustar arthurarthurARCHIV := spu-tools.tar.gz CC = gcc CFLAGS = -g -Wall PREFIX = $(DESTDIR)/usr objs = ctx-info.o spu-info.o proc-info.o general-info.o spu-top.o spu-ps.o target = spu-top spu-ps all: $(target) man ctx-info.o: ctx-info.c spu-tools.h spu-info.o: spu-info.c spu-tools.h proc-info.o: proc-info.c spu-tools.h general-info.o: general-info.c spu-tools.h spu-top.o: spu-top.c spu-tools.h spu-ps.o: spu-ps.c spu-tools.h spu-top: ctx-info.o spu-info.o proc-info.o general-info.o spu-top.o $(CC) $(CFLAGS) -lncurses ctx-info.o spu-info.o proc-info.o general-info.o spu-top.o -o spu-top spu-ps: ctx-info.o spu-ps.o $(CC) $(CFLAGS) ctx-info.o spu-ps.o -o spu-ps clean: @rm -Rf *.o *~ $(objs) $(target) spu-top.1 spu-ps.1 @rm -f .rpmmacros man: spu-ps spu-top help2man --no-info ./spu-top -o spu-top.1 help2man --no-info ./spu-ps -o spu-ps.1 install: echo Installing at $(PREFIX) mkdir -p $(PREFIX)/bin cp spu-top spu-ps $(PREFIX)/bin mkdir -p $(PREFIX)/share/man/man1 cp spu-top.1 spu-ps.1 $(PREFIX)/share/man/man1/ spu-tools-2.3.0.136/src/proc-info.c0000644000202400020240000002205010703676340016207 0ustar arthurarthur/* * Copyright (C) 2007 IBM Corp. * * Author: Andre Detsch * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "spu-tools.h" #include #include #include #include #include #include #include #include #include #include #include /*********************************************** * PER-PROCESS INFORMATION ***********************************************/ struct field proc_fields[] = { { PROC_PPU_PID, "PID", "%6s", "%6d", "PPU Side Process ID", 1, "PPU_PID" }, { PROC_N_THREADS, "N", "%3s", "%3d", "Number of SPE Threads", 1, "N_THREADS" }, { PROC_USER, "USERNAME", " %-10s", " %-10s", "User", 1, "USER_NAME" }, { PROC_PERCENT_SPU, "%SPU", "%7s", "%7.1f", "SPU Usage", 1, "PERCENT_SPU" }, { PROC_TOTAL_TIME, "TIME", "%9s", "%9.3f", "Resident Time (loaded or running) in seconds", 1, "TOTAL_TIME" }, { PROC_USER_TIME, "USER", "%9s", "%9.3f", "User Time in seconds", 0, "USER_TIME" }, { PROC_SYSTEM_TIME, "SYS", "%9s", "%9.3f", "System Time in seconds", 0, "SYSTEM_TIME" }, { PROC_IOWAIT_TIME, "WAIT", "%9s", "%9.3f", "IO/Wait Time in seconds", 0, "IOWAIT_TIME" }, { PROC_LOADED_TIME, "LOAD", "%9s", "%9.3f", "Loaded Time in seconds", 0, "LOADED_TIME" }, { PROC_VOLUNTARY_CTX_SWITCHES, "VCSW", "%5s", "%5llu", "Number of Voluntary Context Switches", 0, "VOLUNTARY_CTX_SWITCHES" }, { PROC_INVOLUNTARY_CTX_SWITCHES, "ICSW", "%5s", "%5llu", "Number of Involuntary Context Switches", 0, "INVOLUNTARY_CTX_SWITCHES" }, { PROC_SLB_MISSES, "SLB", "%5s", "%5llu", "Number of SLB Misses", 0, "SLB_MISSES"}, { PROC_HASH_FAULTS, "HFLT", "%5s", "%5llu", "Number of Hash Faults", 0, "HASH_FAULTS" }, { PROC_MINOR_PAGE_FAULTS, "mFLT", "%5s", "%5llu", "Number of Minor Page Faults", 0, "MINOR_PAGE_FAULTS" }, { PROC_MAJOR_PAGE_FAULTS, "MFLT", "%5s", "%5llu", "Number of Major Page Faults", 0, "MAJOR_PAGE_FAULTS" }, { PROC_CLASS2_INTERRUPTS, "IRQ2", "%5s", "%5llu", "Number of Class2 Interrupts Received", 0, "CLASS2_INTERRUPTS" }, { PROC_PPE_LIBRARY, "PPE_LIB", "%8s", "%8llu", "Number of PPE Assisted Library Performed", 0, "PPE_LIBRARY" }, { PROC_BINARY_NAME, "BINARY", " %-18s", " %-18s", "Binary Name", 1, "BINARY_NAME" }, { 0, NULL, NULL , NULL, NULL, 0, NULL } }; static int proc_sort_descending; static enum proc_field_id proc_sort_field = PROC_PPU_PID; inline enum proc_field_id get_proc_sort_field() { return proc_sort_field; } inline void set_proc_sort_field(enum proc_field_id field) { proc_sort_field = field; } void set_proc_sort_descending(int descending) { proc_sort_descending = descending; } #define DEFAULT_CAPACITY 32 static struct proc** procs; static int procs_n; static int procs_capacity; static int procs_ensure_capacity(int n) { while (procs_capacity < n) { void* ret; ret = realloc(procs, sizeof(struct ctx*) * (procs_capacity + DEFAULT_CAPACITY)); if (!ret) exit(-ENOMEM); procs = ret; procs_capacity += DEFAULT_CAPACITY; } return procs_capacity; } static int procs_compare(const void *v1, const void *v2) { int ret; const struct proc *p1, *p2; p1 = *(struct proc * const *)v1; p2 = *(struct proc * const *)v2; switch (proc_sort_field) { case PROC_PPU_PID: ret = p1->ppu_pid - p2->ppu_pid; break; case PROC_USER: ret = strcmp(p1->user, p2->user); break; case PROC_PERCENT_SPU: ret = p1->percent_spu - p2->percent_spu; break; case PROC_TOTAL_TIME: ret = (p1->time[TIME_TOTAL] - p2->time[TIME_TOTAL]); break; case PROC_USER_TIME: ret = (p1->time[TIME_USER] - p2->time[TIME_USER]); break; case PROC_SYSTEM_TIME: ret = (p1->time[TIME_SYSTEM] - p2->time[TIME_SYSTEM]); break; case PROC_IOWAIT_TIME: ret = (p1->time[TIME_IOWAIT] - p2->time[TIME_IOWAIT]); break; case PROC_LOADED_TIME: ret = (p1->time[TIME_LOADED] - p2->time[TIME_LOADED]); break; case PROC_BINARY_NAME: ret = strcmp(p1->binary_name, p2->binary_name); break; case PROC_VOLUNTARY_CTX_SWITCHES: ret = p1->voluntary_ctx_switches - p2->voluntary_ctx_switches; break; case PROC_INVOLUNTARY_CTX_SWITCHES: ret = p1->involuntary_ctx_switches - p2->involuntary_ctx_switches; break; case PROC_SLB_MISSES: ret = p1->slb_misses - p2->slb_misses; break; case PROC_HASH_FAULTS: ret = p1->hash_faults - p2->hash_faults; break; case PROC_MINOR_PAGE_FAULTS: ret = p1->minor_page_faults - p2->minor_page_faults; break; case PROC_MAJOR_PAGE_FAULTS: ret = p1->major_page_faults - p2->major_page_faults; break; case PROC_CLASS2_INTERRUPTS: ret = p1->class2_interrupts - p2->class2_interrupts; break; case PROC_PPE_LIBRARY: ret = p1->ppe_library - p2->ppe_library; break; default: ret = 0; } return proc_sort_descending? -ret : ret; } struct proc **get_procs(struct ctx** ctxs) { int i, j, proc_idx; enum time time; if (!ctxs) return NULL; for (i = 0; ctxs[i]; i++) ctxs[i]->updated = 0; for (i = 0, proc_idx = 0; ctxs[i]; i++) { if (ctxs[i]->updated) continue; procs_ensure_capacity(proc_idx + 1); if (proc_idx >= procs_n) { procs[proc_idx] = malloc(sizeof(struct proc)); procs_n = proc_idx + 1; } bzero(procs[proc_idx], sizeof(struct proc)); procs[proc_idx]->binary_name = ctxs[i]->binary_name; procs[proc_idx]->user = ctxs[i]->user; procs[proc_idx]->ppu_pid = ctxs[i]->ppu_pid; for (j = i; ctxs[j]; j++) { if (ctxs[i]->ppu_pid != ctxs[j]->ppu_pid) continue; for_each_time(time) procs[proc_idx]->time[time] += ctxs[j]->time[time]; procs[proc_idx]->percent_spu += ctxs[j]->percent_spu; procs[proc_idx]->voluntary_ctx_switches += ctxs[j]->voluntary_ctx_switches; procs[proc_idx]->involuntary_ctx_switches += ctxs[j]->involuntary_ctx_switches; procs[proc_idx]->slb_misses += ctxs[j]->slb_misses; procs[proc_idx]->hash_faults += ctxs[j]->hash_faults; procs[proc_idx]->minor_page_faults += ctxs[j]->minor_page_faults; procs[proc_idx]->major_page_faults += ctxs[j]->major_page_faults; procs[proc_idx]->class2_interrupts += ctxs[j]->class2_interrupts; procs[proc_idx]->ppe_library += ctxs[j]->ppe_library; procs[proc_idx]->n_threads++; ctxs[j]->updated = 1; } proc_idx++; } int old_procs_n = procs_n; for (i = proc_idx; i < old_procs_n; i++) { assert(procs[i]); free(procs[i]); procs[i] = NULL; procs_n--; } procs_ensure_capacity(proc_idx + 1); procs[proc_idx] = NULL; qsort(procs, procs_n, sizeof(struct proc *), procs_compare); return procs; } int print_proc_field(struct proc *proc, char *buf, enum proc_field_id field, const char *format) { switch(field) { case PROC_PPU_PID: return sprintf(buf, format, proc->ppu_pid); case PROC_N_THREADS: return sprintf(buf, format, proc->n_threads); case PROC_USER: return sprintf(buf, format, proc->user); case PROC_PERCENT_SPU: return sprintf(buf, format, proc->percent_spu); case PROC_TOTAL_TIME: return sprintf(buf, format, proc->time[TIME_TOTAL] / 1000.0); case PROC_USER_TIME: return sprintf(buf, format, proc->time[TIME_USER] / 1000.0); case PROC_SYSTEM_TIME: return sprintf(buf, format, proc->time[TIME_SYSTEM] / 1000.0); case PROC_IOWAIT_TIME: return sprintf(buf, format, proc->time[TIME_IOWAIT] / 1000.0); case PROC_LOADED_TIME: return sprintf(buf, format, proc->time[TIME_LOADED] / 1000.0); case PROC_BINARY_NAME: return sprintf(buf, format, proc->binary_name); case PROC_VOLUNTARY_CTX_SWITCHES: return sprintf(buf, format, proc->voluntary_ctx_switches); case PROC_INVOLUNTARY_CTX_SWITCHES: return sprintf(buf, format, proc->involuntary_ctx_switches); case PROC_SLB_MISSES: return sprintf(buf, format, proc->slb_misses); case PROC_HASH_FAULTS: return sprintf(buf, format, proc->hash_faults); case PROC_MINOR_PAGE_FAULTS: return sprintf(buf, format, proc->minor_page_faults); case PROC_MAJOR_PAGE_FAULTS: return sprintf(buf, format, proc->major_page_faults); case PROC_CLASS2_INTERRUPTS: return sprintf(buf, format, proc->class2_interrupts); case PROC_PPE_LIBRARY: return sprintf(buf, format, proc->ppe_library); default : return 0; } } spu-tools-2.3.0.136/src/ctx-info.c0000644000202400020240000003426311117752645016056 0ustar arthurarthur/* * Copyright (C) 2007 IBM Corp. * * Author: Andre Detsch * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "spu-tools.h" #include #include #include #include #include #include #include #include #include #include #include #include float PERCENT(u64 old_t, u64 new_t, u64 tot_t) { float ret = ((tot_t) > 0)? ((new_t) - (old_t)) * 100.0 / (tot_t) : 0.0; return ret <= 100.0? ret : 100.0; } static int ctx_sort_descending; static enum ctx_field_id ctx_sort_field = CTX_PPU_PID; void set_ctx_sort_descending(int descending) { ctx_sort_descending = descending; } inline void set_ctx_sort_field(enum ctx_field_id field) { ctx_sort_field = field; } inline enum ctx_field_id get_ctx_sort_field() { return ctx_sort_field; } /********************************************** * PER-CTX INFORMATION **********************************************/ #define DEFAULT_CAPACITY 32 static struct ctx** ctxs; static int ctxs_n; static int ctxs_capacity; /* USERNAME CACHE */ struct uid_name { const char *name; int uid; struct uid_name *next; }; static struct uid_name *uid_name_head; static void add_to_cache(int uid, const char *name) { struct uid_name *un; un = malloc(sizeof(*un)); un->uid = uid; un->name = name; un->next = uid_name_head; uid_name_head = un; } static const char *lookup_uid_cache(int uid) { struct uid_name *un; for (un = uid_name_head; un; un = un->next) if (un->uid == uid) return un->name; return NULL; } static const char *get_username(int uid) { const char *name; name = lookup_uid_cache(uid); if (!name) { struct passwd *user_data = getpwuid(uid); if (user_data) name = strdup(user_data->pw_name); else name = "UNKNOWN"; add_to_cache(uid, name); } return name; } struct field ctx_fields[] = { { CTX_PPU_PID, "PID", "%6s", "%6d", "PPU Side Process ID", 1, "PPU_PID" }, { CTX_THREAD_ID, "TID", "%6s", "%6d", "SPE Controlling Thread ID", 1, "THREAD_ID" }, { CTX_USER, "USERNAME", " %-10s", " %-10s", "User", 1, "USER_NAME" }, { CTX_STATUS, "S", "%2s", "%2c", "Context Status", 1, "STATUS" }, { CTX_FLAGS, "F", "%2s", "%2c", "Context Flags", 1, "FLAGS" }, { CTX_PERCENT_SPU, "%SPU", "%6s", "%6.1f", "SPU Usage", 1, NULL }, { CTX_SPE, "SPE", "%4s", "%4d", "Allocated SPE", 1, "SPE" }, { CTX_TOTAL_TIME, "TIME", "%9s", "%9.3f", "Resident Time (loaded or running) in seconds", 1, "TOTAL_TIME" }, { CTX_USER_TIME, "USER", "%9s", "%9.3f", "User Time in seconds", 0, "USER_TIME" }, { CTX_SYSTEM_TIME, "SYS", "%9s", "%9.3f", "System Time in seconds", 0, "SYSTEM_TIME" }, { CTX_LOADED_TIME, "LOAD", "%9s", "%9.3f", "Loaded Time (loaded or running) in seconds", 0, "LOADED_TIME" }, { CTX_VOLUNTARY_CTX_SWITCHES, "VCSW", "%5s", "%5llu", "Number of Voluntary Context Switches", 0, "VOLUNTARY_CTX_SWITCHES" }, { CTX_INVOLUNTARY_CTX_SWITCHES, "ICSW", "%5s", "%5llu", "Number of Involuntary Context Switches", 0, "INVOLUNTARY_CTX_SWITCHES" }, { CTX_SLB_MISSES, "SLB", "%5s", "%5llu", "Number of SLB Misses", 0, "SLB_MISSES"}, { CTX_HASH_FAULTS, "HFLT", "%5s", "%5llu", "Number of Hash Faults", 0, "HASH_FAULTS" }, { CTX_MINOR_PAGE_FAULTS, "mFLT", "%5s", "%5llu", "Number of Minor Page Faults", 0, "MINOR_PAGE_FAULTS" }, { CTX_MAJOR_PAGE_FAULTS, "MFLT", "%5s", "%5llu", "Number of Major Page Faults", 0, "MAJOR_PAGE_FAULTS" }, { CTX_CLASS2_INTERRUPTS, "IRQ2", "%5s", "%5llu", "Number of Class2 Interrupts Received", 0, "CLASS2_INTERRUPTS" }, { CTX_PPE_LIBRARY, "PPE_LIB", "%8s", "%8llu", "Number of PPE Assisted Library Calls Performed", 0, "PPE_LIBRARY" }, { CTX_BINARY_NAME, "BINARY", " %-18s", " %-18s", "Binary Name", 1, "BINARY_NAME" }, { 0, NULL, NULL , NULL, NULL, 0, NULL } }; int print_ctx_field(struct ctx *ctx, char *buf, enum ctx_field_id field, const char *format) { switch(field) { case CTX_PPU_PID: return sprintf(buf, format, ctx->ppu_pid); case CTX_THREAD_ID: return sprintf(buf, format, ctx->thread_id); case CTX_USER: return sprintf(buf, format, ctx->user); case CTX_STATUS: return sprintf(buf, format, ctx->status); case CTX_FLAGS: return sprintf(buf, format, ctx->flags); case CTX_PERCENT_SPU: return sprintf(buf, format, ctx->percent_spu); case CTX_SPE: return sprintf(buf, format, ctx->spe); case CTX_TOTAL_TIME: return sprintf(buf, format, ctx->time[TIME_TOTAL] / 1000.0); case CTX_USER_TIME: return sprintf(buf, format, ctx->time[TIME_USER] / 1000.0); case CTX_SYSTEM_TIME: return sprintf(buf, format, ctx->time[TIME_SYSTEM] / 1000.0); case CTX_LOADED_TIME: return sprintf(buf, format, ctx->time[TIME_LOADED] / 1000.0); case CTX_BINARY_NAME: return sprintf(buf, format, ctx->binary_name); case CTX_VOLUNTARY_CTX_SWITCHES: return sprintf(buf, format, ctx->voluntary_ctx_switches); case CTX_INVOLUNTARY_CTX_SWITCHES: return sprintf(buf, format, ctx->involuntary_ctx_switches); case CTX_SLB_MISSES: return sprintf(buf, format, ctx->slb_misses); case CTX_HASH_FAULTS: return sprintf(buf, format, ctx->hash_faults); case CTX_MINOR_PAGE_FAULTS: return sprintf(buf, format, ctx->minor_page_faults); case CTX_MAJOR_PAGE_FAULTS: return sprintf(buf, format, ctx->major_page_faults); case CTX_CLASS2_INTERRUPTS: return sprintf(buf, format, ctx->class2_interrupts); case CTX_PPE_LIBRARY: return sprintf(buf, format, ctx->ppe_library); default : return 0; } } static int ctxs_compare(const void *v1, const void *v2) { int ret; const struct ctx *p1, *p2; p1 = *(struct ctx * const *)v1; p2 = *(struct ctx * const *)v2; switch (ctx_sort_field) { case CTX_PPU_PID: ret = p1->ppu_pid - p2->ppu_pid; break; case CTX_THREAD_ID: ret = p1->thread_id - p2->thread_id; break; case CTX_USER: ret = strcmp(p1->user, p2->user); break; case CTX_STATUS: ret = p1->status - p2->status; break; case CTX_FLAGS: ret = p1->flags - p2->flags; break; case CTX_PERCENT_SPU: ret = p1->percent_spu - p2->percent_spu; break; case CTX_SPE: ret = p1->spe - p2->spe; break; case CTX_TOTAL_TIME: ret = p1->time[TIME_TOTAL] - p2->time[TIME_TOTAL]; break; case CTX_USER_TIME: ret = p1->time[TIME_USER] - p2->time[TIME_USER]; break; case CTX_SYSTEM_TIME: ret = p1->time[TIME_SYSTEM] - p2->time[TIME_SYSTEM]; break; case CTX_LOADED_TIME: ret = p1->time[TIME_LOADED] - p2->time[TIME_LOADED]; break; case CTX_BINARY_NAME: ret = strcmp(p1->binary_name, p2->binary_name); break; case CTX_VOLUNTARY_CTX_SWITCHES: ret = p1->voluntary_ctx_switches - p2->voluntary_ctx_switches; break; case CTX_INVOLUNTARY_CTX_SWITCHES: ret = p1->involuntary_ctx_switches - p2->involuntary_ctx_switches; break; case CTX_SLB_MISSES: ret = p1->slb_misses - p2->slb_misses; break; case CTX_HASH_FAULTS: ret = p1->hash_faults - p2->hash_faults; break; case CTX_MINOR_PAGE_FAULTS: ret = p1->minor_page_faults - p2->minor_page_faults; break; case CTX_MAJOR_PAGE_FAULTS: ret = p1->major_page_faults - p2->major_page_faults; break; case CTX_CLASS2_INTERRUPTS: ret = p1->class2_interrupts - p2->class2_interrupts; break; case CTX_PPE_LIBRARY: ret = p1->ppe_library - p2->ppe_library; break; default: ret = 0; } return ctx_sort_descending? -ret : ret; } static struct ctx *alloc_ctx() { struct ctx *s; s = (struct ctx *)calloc(1, sizeof(struct ctx)); s->ppu_pid = 0; s->status = '?'; return s; } static int ctxs_ensure_capacity(int n) { while (ctxs_capacity < n) { void* ret; ret = realloc(ctxs, sizeof(struct ctx*) * (ctxs_capacity + DEFAULT_CAPACITY)); if (!ret) exit(-ENOMEM); ctxs = ret; ctxs_capacity += DEFAULT_CAPACITY; } return ctxs_capacity; } static struct ctx *ctxs_get_ctx(int thread_id) { int i; /* This could be optimized by using a hash table */ for (i = 0; i < ctxs_n; i++) { if (ctxs[i]->thread_id == thread_id) return ctxs[i]; } return NULL; } static int get_thread_pid(int thread_id, const char* entry_name) { DIR* proc_dir; struct dirent *entry; static char buf[PATH_MAX]; unsigned long long ctx_id; int pid = -1, pid_entry = -1; /* Try to get PID from the spufs entry name */ if (sscanf(entry_name, "spethread-%d-%llu", &pid_entry, &ctx_id) == 2) { /* Check if the TID belongs to the given PID in procfs */ sprintf(buf, "%s/%d/task/%d", PROCFS_PATH, pid_entry, thread_id); if (access(buf, F_OK) == 0) return pid_entry; } /* If PID was not found yet, try to find it in procfs */ proc_dir = opendir(PROCFS_PATH); if (proc_dir) { while ((entry = readdir(proc_dir)) != NULL) { sprintf(buf, "%s/%s/task/%d", PROCFS_PATH, entry->d_name, thread_id); if (access(buf, F_OK) == 0) { sscanf(entry->d_name, "%d", &pid); break; } } closedir(proc_dir); } if (pid != -1) return pid; /* If no PID was found, use the one given by the entry name, even * if it can't be verified in procfs */ return pid_entry; } static int process_ctx_entry(struct dirent *entry, const char *gang_name, u64 last_period) { static char buf[PATH_MAX]; FILE *fp; int uid, thread_id; struct ctx *ctx; if (gang_name) sprintf(buf, "%s/%s/%s", SPUFS_PATH, gang_name, entry->d_name); else sprintf(buf, "%s/%s", SPUFS_PATH, entry->d_name); if (chdir(buf)) return 0; fp = fopen("tid", "r"); if (fp) { fscanf(fp, "%d", &thread_id); fclose(fp); } else { return 0; } ctx = ctxs_get_ctx(thread_id); if (!ctx) { ctxs_ensure_capacity(ctxs_n + 1); ctx = alloc_ctx(); ctxs[ctxs_n] = ctx; ctxs_n++; ctx->thread_id = thread_id; ctx->ppu_pid = get_thread_pid(thread_id, entry->d_name); } sprintf(buf, "%s/%d/stat", PROCFS_PATH, ctx->ppu_pid); fp = fopen(buf, "r"); if (fp) { fscanf(fp, "%d %s", &ctx->ppu_pid, buf); buf[strlen(buf)-1] = '\0'; /* Remove trailing ')'*/ ctx->binary_name = strdup(buf+1); /* Skip initial '(' */ fclose(fp); } sprintf(buf, "%s/%d/status", PROCFS_PATH, ctx->ppu_pid); fp = fopen(buf, "r"); if (fp) { while (fscanf(fp, "%s", buf) != EOF) { if (!strncmp(buf, "Uid:", 4)) { fscanf(fp, "%d", &uid); ctx->user = get_username(uid); break; } } fclose(fp); } if (access("phys-id", R_OK) != 0) { ctx->spe = SPE_UNKNOWN; } else { fp = fopen("phys-id", "r"); if (fp) { fscanf(fp, "%x", &ctx->spe); fclose(fp); } } fp = fopen("stat", "r"); if (fp) { u64 last_time; last_time = ctx->time[TIME_TOTAL]; fscanf(fp, "%s %llu %llu %llu %llu " "%llu %llu %llu %llu %llu %llu %llu %llu", buf, /* current SPE state */ &ctx->time[TIME_USER], /* total user time in milliseconds */ &ctx->time[TIME_SYSTEM], /* total system time in milliseconds */ &ctx->time[TIME_IOWAIT], /* total iowait time in milliseconds */ &ctx->time[TIME_LOADED], /* total loaded time in milliseconds */ &ctx->voluntary_ctx_switches, /* number of voluntary context switches */ &ctx->involuntary_ctx_switches, /* number of involuntary context switches */ &ctx->slb_misses, /* number of SLB misses */ &ctx->hash_faults, /* number of hash faults */ &ctx->minor_page_faults, /* number of minor page faults */ &ctx->major_page_faults, /* number of major page faults */ &ctx->class2_interrupts, /* number of class2 interrupts received */ &ctx->ppe_library); /* number of ppe assisted library performed */ fclose(fp); ctx->status = toupper(buf[0]); ctx->time[TIME_TOTAL] = ctx->time[TIME_USER] + ctx->time[TIME_SYSTEM] + ctx->time[TIME_IOWAIT]; ctx->percent_spu = PERCENT(last_time, ctx->time[TIME_TOTAL], last_period); } else { ctx->status = '?'; } fp = fopen("capabilities", "r"); if (fp) { ctx->flags = 'I'; while (fscanf(fp, "%s", buf) != EOF) { if (!strcmp(buf, "sched")) { ctx->flags = ' '; break; } } fclose(fp); } else { ctx->flags = '?'; } ctx->updated = 1; chdir(SPUFS_PATH); return 0; } void ctxs_delete(int index) { int i; assert(ctxs[index]); free(ctxs[index]->binary_name); for (i = index; i < ctxs_n; i++) ctxs[i] = ctxs[i + 1]; ctxs_n--; } struct ctx **get_spu_contexts(u64 last_period) { struct dirent *entry, *gang_entry; DIR *ctxs_dir, *gang_dir; int i; static char buf[PATH_MAX]; ctxs_dir = opendir(SPUFS_PATH); if (!ctxs_dir) return NULL; for (i = 0; i < ctxs_n; i++) ctxs[i]->updated = 0; while ((entry = readdir(ctxs_dir)) != NULL) { sprintf(buf, "%s/%s/stat", SPUFS_PATH, entry->d_name); if (access(buf, F_OK) == 0) { process_ctx_entry(entry, NULL, last_period); } else { /* contexts within a gang */ sprintf(buf, "%s/%s", SPUFS_PATH, entry->d_name); gang_dir = opendir(buf); if (gang_dir) { while ((gang_entry = readdir(gang_dir)) != NULL) { sprintf(buf, "%s/%s/%s/stat", SPUFS_PATH, entry->d_name, gang_entry->d_name); if (access(buf, F_OK) == 0) process_ctx_entry(gang_entry, entry->d_name, last_period); } closedir(gang_dir); } } } closedir(ctxs_dir); for (i = 0; i < ctxs_n; i++) { if (!ctxs[i]->updated) ctxs_delete(i); } ctxs_ensure_capacity(ctxs_n + 1); ctxs[ctxs_n] = NULL; qsort(ctxs, ctxs_n, sizeof(struct ctx *), ctxs_compare); return ctxs; } spu-tools-2.3.0.136/src/spu-info.c0000644000202400020240000002032011021773271016044 0ustar arthurarthur/* * Copyright (C) 2006 IBM Corp. * * Author: Andre Detsch * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "spu-tools.h" #include #include #include #include #include #include #include #include #include #include #include #include /********************************************** * SORTING CRITERIA **********************************************/ static int spu_sort_descending; static enum spu_field_id spu_sort_field = SPU_NUMBER; void set_spu_sort_descending(int descending) { spu_sort_descending = descending; } inline void set_spu_sort_field(enum spu_field_id field) { spu_sort_field = field; } inline enum spu_field_id get_spu_sort_field() { return spu_sort_field; } /*********************************************** * PER-SPU INFORMATION ***********************************************/ static struct spu** spus; static int spus_n; static int spus_compare(const void *v1, const void *v2) { const struct spu *p1, *p2; int ret; p1 = *(struct spu * const *)v1; p2 = *(struct spu * const *)v2; switch (spu_sort_field) { case SPU_NUMBER: ret = p1->number - p2->number; break; case SPU_STATE: ret = p1->state - p2->state; break; case SPU_PERCENT_SPU: ret = p1->percent[TIME_TOTAL] - p2->percent[TIME_TOTAL]; break; case SPU_PERCENT_USER: ret = p1->percent[TIME_USER] - p2->percent[TIME_USER]; break; case SPU_PERCENT_SYSTEM: ret = p1->percent[TIME_SYSTEM] - p2->percent[TIME_SYSTEM]; break; case SPU_PERCENT_IOWAIT: ret = p1->percent[TIME_IOWAIT] - p2->percent[TIME_IOWAIT]; break; case SPU_VOLUNTARY_CTX_SWITCHES: ret = p1->voluntary_ctx_switches - p2->voluntary_ctx_switches; break; case SPU_INVOLUNTARY_CTX_SWITCHES: ret = p1->involuntary_ctx_switches - p2->involuntary_ctx_switches; break; case SPU_SLB_MISSES: ret = p1->slb_misses - p2->slb_misses; break; case SPU_HASH_FAULTS: ret = p1->hash_faults - p2->hash_faults; break; case SPU_MINOR_PAGE_FAULTS: ret = p1->minor_page_faults - p2->minor_page_faults; break; case SPU_MAJOR_PAGE_FAULTS: ret = p1->major_page_faults - p2->major_page_faults; break; case SPU_CLASS2_INTERRUPTS: ret = p1->class2_interrupts - p2->class2_interrupts; break; case SPU_PPE_LIBRARY: ret = p1->ppe_library - p2->ppe_library; break; case SPU_CTX_THREAD_ID: ret = p1->ctx_thread_id - p2->ctx_thread_id; break; default : ret = 0; break; } return spu_sort_descending? -ret : ret;; } static int spu_update(struct spu *spu) { FILE* fd; u64 period; char buf[PATH_MAX]; enum time time; spu->state = '?'; sprintf(buf, "%s/devices/system/spu/spu%d/stat", SYSFS_PATH, spu->number); fd = fopen(buf, "r"); if (!fd) return -1; for_each_time(time) spu->last_time[time] = spu->time[time]; fscanf(fd, "%s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", buf, &spu->time[TIME_USER], &spu->time[TIME_SYSTEM], &spu->time[TIME_IOWAIT], &spu->time[TIME_IDLE], &spu->voluntary_ctx_switches, &spu->involuntary_ctx_switches, &spu->slb_misses, &spu->hash_faults, &spu->minor_page_faults, &spu->major_page_faults, &spu->class2_interrupts, &spu->ppe_library); fclose(fd); spu->state = toupper(buf[0]); spu->time[TIME_TOTAL] = spu->time[TIME_USER] + spu->time[TIME_SYSTEM] + spu->time[TIME_IOWAIT]; period = (spu->time[TIME_TOTAL] - spu->last_time[TIME_TOTAL]) + (spu->time[TIME_IDLE] - spu->last_time[TIME_IDLE]); for_each_time(time) spu->percent[time] = PERCENT(spu->last_time[time], spu->time[time], period); return 0; } struct spu **get_spus() { struct dirent *entry; int i; DIR *spus_dir; if (!spus) { spus_n = count_spus(); spus = malloc(sizeof(struct spu*) * (spus_n + 1)); for (i = 0; i < spus_n; i++) spus[i] = calloc(1, sizeof(struct spu)); spus[spus_n] = NULL; i = 0; spus_dir = opendir(SYSFS_PATH "/devices/system/spu"); if (!spus_dir) { return spus; } while ((entry = readdir(spus_dir)) != NULL) { if (!strncmp(entry->d_name, "spu", 3)) { int ret; assert(i < spus_n); ret = sscanf(entry->d_name, "spu%d", &spus[i]->number); assert(ret >= 1); i++; } } closedir(spus_dir); } assert(spus); for (i = 0; i < spus_n; i++) { spu_update(spus[i]); } qsort(spus, spus_n, sizeof(struct spu *), spus_compare); return spus; } struct field spu_fields[] = { { SPU_NUMBER, "SPE", "%3s", "%3d", "SPE Number", 1, "" }, { SPU_PERCENT_SPU, "%SPU", "%6s", "%6.1f", "SPU Usage", 1, "" }, { SPU_PERCENT_USER, "%USR", "%6s", "%6.1f", "SPU User", 1, "" }, { SPU_PERCENT_SYSTEM, "%SYS", "%6s", "%6.1f", "SPU System", 1, "" }, { SPU_PERCENT_IOWAIT, "%WAI", "%6s", "%6.1f", "SPU I/O Wait", 1, "" }, { SPU_STATE, "S", "%2s", "%2c", "State", 1, "" }, { SPU_VOLUNTARY_CTX_SWITCHES, "VCSW", "%6s", "%6llu", "Number of Voluntary Context Switches", 0, "" }, { SPU_INVOLUNTARY_CTX_SWITCHES, "ICSW", "%6s", "%6llu", "Number of Involuntary Context Switches", 0, "" }, { SPU_SLB_MISSES, "SLB", "%6s", "%6llu", "Number of SLB Misses", 1, "" }, { SPU_HASH_FAULTS, "HFLT", "%6s", "%6llu", "Number of Hash Faults", 1, "" }, { SPU_MINOR_PAGE_FAULTS, "mFLT", "%6s", "%6llu", "Number of Minor Page Faults", 1, "" }, { SPU_MAJOR_PAGE_FAULTS, "MFLT", "%6s", "%6llu", "Number of Major Page Faults", 1, "" }, { SPU_CLASS2_INTERRUPTS, "IRQ2", "%6s", "%6llu", "Number of Class2 Interrupts Received", 1, "" }, { SPU_PPE_LIBRARY, "PPE_LIB", "%8s", "%8llu", "Number of PPE Assisted Library Calls Performed", 1, "" }, { SPU_CTX_THREAD_ID, "TID", "%6s", "%6d", "SPE Controlling Thread ID", 0, "" }, { 0, NULL, NULL, NULL, NULL, 0 } }; int print_spu_field(struct spu *spu, char *buf, enum spu_field_id field, const char *format) { switch(field) { case SPU_NUMBER: return sprintf(buf, format, spu->number); case SPU_STATE: return sprintf(buf, format, spu->state); case SPU_PERCENT_SPU: return sprintf(buf, format, spu->percent[TIME_TOTAL]); case SPU_PERCENT_USER: return sprintf(buf, format, spu->percent[TIME_USER]); case SPU_PERCENT_SYSTEM: return sprintf(buf, format, spu->percent[TIME_SYSTEM]); case SPU_PERCENT_IOWAIT: return sprintf(buf, format, spu->percent[TIME_IOWAIT]); case SPU_VOLUNTARY_CTX_SWITCHES: return sprintf(buf, format, spu->voluntary_ctx_switches); case SPU_INVOLUNTARY_CTX_SWITCHES: return sprintf(buf, format, spu->involuntary_ctx_switches); case SPU_SLB_MISSES: return sprintf(buf, format, spu->slb_misses); case SPU_HASH_FAULTS: return sprintf(buf, format, spu->hash_faults); case SPU_MINOR_PAGE_FAULTS: return sprintf(buf, format, spu->minor_page_faults); case SPU_MAJOR_PAGE_FAULTS: return sprintf(buf, format, spu->major_page_faults); case SPU_CLASS2_INTERRUPTS: return sprintf(buf, format, spu->class2_interrupts); case SPU_PPE_LIBRARY: return sprintf(buf, format, spu->ppe_library); case SPU_CTX_THREAD_ID: return sprintf(buf, format, spu->ctx_thread_id); default: return 0; } } void fill_spus_tids(struct spu** spus, struct ctx** ctxs) { int i, j; for (j = 0; spus[j]; j++) spus[j]->ctx_thread_id = 0; for (i = 0; ctxs[i]; i++) { int spe = ctxs[i]->spe; if (spe < 0) continue; for (j = 0; spus[j]; j++) { if (spus[j]->number == spe) { spus[j]->ctx_thread_id = ctxs[i]->thread_id; break; } } } } spu-tools-2.3.0.136/src/general-info.c0000644000202400020240000000701710703676340016667 0ustar arthurarthur/* * Copyright (C) 2007 IBM Corp. * * Author: Andre Detsch * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "spu-tools.h" #include #include #include #include #include #include #include #include #include #include #include /*********************************************** * CPUS / SPUS stats ***********************************************/ static u64 last_times[TIME_MAX]; void get_cpu_stats(float *percents) { FILE* fd; static u64 times[TIME_MAX]; enum time time; u64 irq_time, softirq_time, steal_time, period; int ret; fd = fopen(PROCFS_PATH "/stat", "r"); assert(fd); steal_time = 0; ret = fscanf(fd, "cpu %llu %llu %llu %llu %llu %llu %llu %llu", ×[TIME_USER], ×[TIME_NICE], ×[TIME_SYSTEM], ×[TIME_IDLE], ×[TIME_IOWAIT], &irq_time, &softirq_time, &steal_time); assert(ret >= 7); fclose(fd); times[TIME_SYSTEM] += irq_time + softirq_time + steal_time; times[TIME_TOTAL] = times[TIME_USER] + times[TIME_NICE] + times[TIME_SYSTEM] + times[TIME_IDLE] + times[TIME_IOWAIT]; assert(times[TIME_TOTAL] >= last_times[TIME_TOTAL]); period = times[TIME_TOTAL] - last_times[TIME_TOTAL]; for_each_time(time) { percents[time] = PERCENT(last_times[time],times[time],period); last_times[time] = times[time]; } } void get_spu_stats(struct spu** spus, float *percents) { u64 sum[TIME_MAX]; int i; enum time time; if (!spus) return; for_each_time(time) sum[time] = 0; for (i = 0; spus[i]; i++) { for_each_time(time) sum[time] += (spus[i]->time[time] - spus[i]->last_time[time]); } if (i > 0) { for_each_time(time) percents[time] = PERCENT(0, sum[time], sum[TIME_TOTAL]+sum[TIME_IDLE]); } } void get_cpus_loadavg(float *avg1min, float *avg5min, float *avg15min) { FILE* fd; int ret; fd = fopen(PROCFS_PATH "/loadavg", "r"); if (fd) { ret = fscanf(fd, "%f %f %f", avg1min, avg5min, avg15min); fclose(fd); } } void get_spus_loadavg(float *avg1min, float *avg5min, float *avg15min) { FILE* fd; int ret; fd = fopen(PROCFS_PATH "/spu_loadavg", "r"); if (fd) { ret = fscanf(fd, "%f %f %f", avg1min, avg5min, avg15min); fclose(fd); assert(ret >= 3); } } /********************************************** * GENERIC FUNCTIONS **********************************************/ int count_cpus() { int cpus = 0; DIR *dir; struct dirent *entry; dir = opendir(SYSFS_PATH "/devices/system/cpu"); if (!dir) return 0; while ((entry = readdir(dir)) != NULL) if (!strncmp(entry->d_name, "cpu", 3)) cpus++; closedir(dir); return cpus; } int count_spus() { int spus = 0; DIR *dir; struct dirent *entry; dir = opendir(SYSFS_PATH "/devices/system/spu"); if (!dir) return 0; while ((entry = readdir(dir)) != NULL) if (!strncmp(entry->d_name, "spu", 3)) spus++; closedir(dir); return spus; } spu-tools-2.3.0.136/src/spu-top.c0000644000202400020240000003047511021773271015727 0ustar arthurarthur/* * Copyright (C) 2007 IBM Corp. * * Author: Andre Detsch * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "spu-tools.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define BLACK_ON_WHITE 1 #define WHITE_ON_BLACK 2 #define DEFAULT_REFRESH_DELAY 30 #define MAX_LINE_SIZE 1024 static int refresh_delay = DEFAULT_REFRESH_DELAY; static enum screen_mode { PER_CTX, PER_SPU, PER_PROC } screen_mode = PER_CTX; static void print_header(struct field *fields) { int char_count = 0; int i = 0; char buf[MAX_LINE_SIZE]; mvprintw(0, 0, "spu-top: %s View", screen_mode == PER_CTX? "Context" : screen_mode == PER_SPU? "SPU" : "Process"); wbkgdset(stdscr, COLOR_PAIR(BLACK_ON_WHITE)); move(6, 0); clrtoeol(); while (fields[i].id) { if (fields[i].do_show) { char_count += sprintf(buf+char_count, fields[i].name_format, fields[i].name); } i++; } if (char_count) mvaddstr(6, 0, buf); wbkgdset(stdscr, COLOR_PAIR(WHITE_ON_BLACK)); } static void dump_fields(void **table, struct field *fields) { int cnt; int rows, cols; int i; char buf[MAX_LINE_SIZE]; int ofs = 7; getmaxyx(stdscr, rows, cols); if (!table) return; for (cnt = ofs; cnt < rows; cnt++) { move(cnt, 0); clrtoeol(); } cnt = ofs; for (i = 0; table[i] && i < (rows - ofs); i++) { int j; int chars = 0; for (j = 0; fields[j].id; j++) { if (!fields[j].do_show) continue; if (screen_mode == PER_CTX) { chars += print_ctx_field((struct ctx *)table[i], buf+chars, fields[j].id, fields[j].format); } else if (screen_mode == PER_SPU) { chars += print_spu_field((struct spu *)table[i], buf+chars, fields[j].id, fields[j].format); } else { chars += print_proc_field((struct proc *)table[i], buf+chars, fields[j].id, fields[j].format); } } if (chars) mvaddstr(cnt, 0, buf); cnt++; } return; } /* Clean up and leave */ static void quit() { clear(); endwin(); exit(0); } /* Initializes ncurses library */ static void init_ncurses() { /* Init screen */ initscr(); curs_set(0); start_color(); clear(); nodelay(stdscr, TRUE); cbreak(); noecho(); /* Init colors */ init_pair(BLACK_ON_WHITE, COLOR_BLACK, COLOR_WHITE); init_pair(WHITE_ON_BLACK, COLOR_WHITE, COLOR_BLACK); attrset(COLOR_PAIR(WHITE_ON_BLACK)); wbkgdset(stdscr, COLOR_PAIR(WHITE_ON_BLACK)); clear(); return; } void print_cpu_info(int min_time_has_passed) { static float percents[TIME_TOTAL]; static float avg1min, avg5min, avg15min; wbkgdset(stdscr, COLOR_PAIR(WHITE_ON_BLACK)); get_cpus_loadavg(&avg1min, &avg5min, &avg15min); mvprintw(1, 0, "Cpu(s) load avg: %4.2f, %4.2f, %4.2f\n", avg1min, avg5min, avg15min); if (min_time_has_passed) get_cpu_stats(percents); mvprintw(3, 0, "Cpu(s):%5.1f%%us,%5.1f%%sys,%5.1f%%wait,%5.1f%%nice,%5.1f%%idle", percents[TIME_USER], percents[TIME_SYSTEM], percents[TIME_IOWAIT], percents[TIME_NICE], percents[TIME_IDLE]); } void print_spu_info(struct spu** spus, int min_time_has_passed) { static float percents[TIME_TOTAL]; static float avg1min, avg5min, avg15min; wbkgdset(stdscr, COLOR_PAIR(WHITE_ON_BLACK)); get_spus_loadavg(&avg1min, &avg5min, &avg15min); mvprintw(2, 0, "Spu(s) load avg: %4.2f, %4.2f, %4.2f\n", avg1min, avg5min, avg15min); if (min_time_has_passed) get_spu_stats(spus, percents); mvprintw(4, 0, "Spu(s):%5.1f%%us,%5.1f%%sys,%5.1f%%wait,%5.1f%%idle", percents[TIME_USER], percents[TIME_SYSTEM], percents[TIME_IOWAIT], percents[TIME_IDLE]); } static void config_sort(struct field *fields) { int i; char lc; const int ofs = 3; char max_field = screen_mode == PER_CTX? CTX_MAX_FIELD : screen_mode == PER_SPU? SPU_MAX_FIELD : PROC_MAX_FIELD; for (;;) { char sort_field = screen_mode == PER_CTX? get_ctx_sort_field() : screen_mode == PER_SPU? get_spu_sort_field() : get_proc_sort_field(); erase(); mvprintw(1, 0, "Select sort field via field letter, type any other key to return"); for (i = 0; fields[i].id; i++) { mvprintw(i + ofs, 0, "%c %c: %-12s = %s", fields[i].id == sort_field? '*':' ', fields[i].id, fields[i].name, fields[i].description); } nocbreak(); cbreak(); nodelay(stdscr, FALSE); lc = tolower(getch()); if (lc < 'a' || lc > max_field) break; screen_mode == PER_CTX? set_ctx_sort_field(lc) : screen_mode == PER_SPU? set_spu_sort_field(lc) : set_proc_sort_field(lc); } halfdelay(refresh_delay); } static void config_show(struct field *fields) { int i; char lc; int ofs = 3; char max_field = screen_mode == PER_CTX? CTX_MAX_FIELD : screen_mode == PER_SPU? SPU_MAX_FIELD : PROC_MAX_FIELD; for (;;) { erase(); mvprintw(1, 0, "Toggle fields via field letter, type any other key to return"); for (i = 0; fields[i].id; i++) { mvprintw(i + ofs, 0, "%c %c: %-12s = %s", fields[i].do_show? '*':' ', fields[i].id, fields[i].name, fields[i].description); } nocbreak(); cbreak(); nodelay(stdscr, FALSE); lc = tolower(getch()); if (lc < 'a' || lc > max_field) break; for (i = 0; fields[i].id; i++) { if (fields[i].id == lc) { fields[i].do_show = !fields[i].do_show; break; } } } halfdelay(refresh_delay); } static void config_order(struct field *fields) { int i; char c, lc; int ofs = 4; char max_field = screen_mode == PER_CTX? CTX_MAX_FIELD : screen_mode == PER_SPU? SPU_MAX_FIELD : PROC_MAX_FIELD; for (;;) { erase(); mvprintw(1, 0, "Upper case letter moves field left, lower case right"); mvprintw(2, 0, "Any other key to return"); for (i = 0; fields[i].id; i++) { mvprintw(i + ofs, 0, "%c %c: %-12s = %s", fields[i].do_show? '*':' ', fields[i].id, fields[i].name, fields[i].description); } nocbreak(); cbreak(); nodelay(stdscr, FALSE); c = getch(); lc = tolower(c); if (lc < 'a' || lc > max_field) break; for (i = 0; fields[i].id; i++) { if (fields[i].id == lc) { struct field tmp; if (isupper(c) && i > 0) { tmp = fields[i-1]; fields[i-1] = fields[i]; fields[i] = tmp; } else if (islower(c) && fields[i+1].id) { tmp = fields[i+1]; fields[i+1] = fields[i]; fields[i] = tmp; } break; } } } halfdelay(refresh_delay); } static void show_help() { int l = 0; erase(); mvprintw(l++, 0, "Help for Interactive Commands - spu-top"); l++; mvprintw(l++, 0, " %-5s - %s", "f", "add or remove fields/columns"); mvprintw(l++, 0, " %-5s - %s", "o", "adjust fields/columns order"); l++; mvprintw(l++, 0, " %-5s - %s", "O", "set sorting criteria"); mvprintw(l++, 0, " %-5s - %s", "A", "sort in ascending order"); mvprintw(l++, 0, " %-5s - %s", "D", "sort in descending order"); l++; mvprintw(l++, 0, " %-5s - %s", "c", "switch to per-context view"); mvprintw(l++, 0, " %-5s - %s", "p", "switch to per-process view"); mvprintw(l++, 0, " %-5s - %s", "s", "switch to per-spu view"); l++; mvprintw(l++, 0, " %-5s - %s", "h,H,?", "displays this help screen"); mvprintw(l++, 0, " %-5s - %s", "q,Q", "quit program"); l++; mvprintw(l, 0, "Press any key to return."); nocbreak(); cbreak(); nodelay(stdscr, FALSE); getch(); halfdelay(refresh_delay); } static void version() { printf( "spu-top (spu-tools 1.0)\n\n" "Copyright (C) IBM 2007.\n" "Released under the GNU GPL.\n\n" ); } static void usage() { printf( "Usage: spu-top [OPTIONS]\n" "The spu-top program provides a dynamic real-time view of the\n" "running system in regards to Cell/B.E. SPUs. It provides 3 view modes:""\n\n" "* Per-Context: information about all instantiated SPU contexts.""\n\n" "* Per-Process: same information as in Per-Context view, but consolidating\n" "all data from the contexts belonging to a same process in a single line.\n" "That means that statistics such as spu usage may reach number_of_spus * 100%%.\n\n" "* Per-SPU: information about each physical SPU present on the system.""\n\n" "Selection of mode and shown fields can be done within the program.""\n" "Press 'h' while running spu-top for help on the interactive commands.""\n\n" "Options:\n" " -h, --help display this help and exit\n" " -v, --version output version information and exit\n\n" ); } int main(int argc, char **argv) { int c; u64 period; int do_quit = 0; struct spu** spus; struct proc** procs; struct ctx** ctxs; struct timeval last_time, current_time; char* term; /* Parse options */ static struct option long_options[] = { {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {0, 0, 0, 0} }; while (1) { c = getopt_long(argc, argv, "hv", long_options, NULL); if (c == -1) break; switch(c) { case 'v': version(); exit(0); default: usage(); exit(0); } } /* Init ncurses library */ init_ncurses(); halfdelay(refresh_delay); keypad(stdscr, true); term = getenv("TERM"); if (!strcmp(term, "xterm") || !strcmp(term, "xterm-color") || !strcmp(term, "vt220")) { define_key("\033[H", KEY_HOME); define_key("\033[F", KEY_END); define_key("\033OP", KEY_F(1)); define_key("\033OQ", KEY_F(2)); define_key("\033OR", KEY_F(3)); define_key("\033OS", KEY_F(4)); define_key("\033[11~", KEY_F(1)); define_key("\033[12~", KEY_F(2)); define_key("\033[13~", KEY_F(3)); define_key("\033[14~", KEY_F(4)); define_key("\033[17;2~", KEY_F(18)); } /* Handle signals */ atexit(quit); signal(SIGALRM, quit); signal(SIGHUP, quit); signal(SIGINT, quit); signal(SIGPIPE, quit); signal(SIGQUIT, quit); signal(SIGTERM, quit); last_time.tv_sec = 0; last_time.tv_usec = 0; /* Providing a valid time range for the first measure (0.055 sec) */ spus = get_spus(); ctxs = get_spu_contexts(refresh_delay); procs = get_procs(ctxs); usleep(55000); while (!do_quit) { int ch; int min_time_has_passed; erase(); gettimeofday(¤t_time, NULL); /* Update data only if time interval > 100ms */ period = (u64)((double)current_time.tv_sec*1000 + (double)current_time.tv_usec/1000) - ((double)last_time.tv_sec*1000 + (double)last_time.tv_usec/1000); min_time_has_passed = period > 100; if (min_time_has_passed) { spus = get_spus(); ctxs = get_spu_contexts(period); procs = get_procs(ctxs); fill_spus_tids(spus, ctxs); last_time = current_time; } print_cpu_info(min_time_has_passed); print_spu_info(spus, min_time_has_passed); if (screen_mode == PER_CTX) { print_header(ctx_fields); dump_fields((void**)ctxs, ctx_fields); } else if (screen_mode == PER_SPU) { print_header(spu_fields); dump_fields((void**)spus, spu_fields); } else { print_header(proc_fields); dump_fields((void**)procs, proc_fields); } move(5, 0); refresh(); ch = getch(); switch (ch) { case 'h': case 'H': case '?': show_help(); break; case 'q': case 'Q': do_quit = 1; break; case 'c': screen_mode = PER_CTX; break; case 's': screen_mode = PER_SPU; break; case 'p': screen_mode = PER_PROC; break; case 'A': screen_mode==PER_CTX? set_ctx_sort_descending(0) : screen_mode==PER_SPU? set_spu_sort_descending(0) : set_proc_sort_descending(0); break; case 'D': screen_mode==PER_CTX? set_ctx_sort_descending(1) : screen_mode==PER_SPU? set_spu_sort_descending(1) : set_proc_sort_descending(1); break; case 'f': config_show( screen_mode==PER_CTX? ctx_fields : screen_mode==PER_SPU? spu_fields : proc_fields); break; case 'o': config_order(screen_mode==PER_CTX? ctx_fields : screen_mode==PER_SPU? spu_fields : proc_fields); break; case 'O': config_sort( screen_mode==PER_CTX? ctx_fields : screen_mode==PER_SPU? spu_fields : proc_fields); break; } } quit(); return 0; }