From: Cliff Wickman <cpw@sgi.com>
To: Bill Gray <bgray@redhat.com>
Cc: linux-numa@vger.kernel.org,
Douglas Shakshober <dshaks@redhat.com>,
Bill Burns <bburns@redhat.com>,
andi@firstfloor.org
Subject: Re: Red Hat would like to rework / enhance numastat
Date: Tue, 5 Jun 2012 07:26:31 -0500 [thread overview]
Message-ID: <20120605122631.GA28054@sgi.com> (raw)
In-Reply-To: <4FCD84AD.6020103@redhat.com>
Hi Bill,
On Tue, Jun 05, 2012 at 12:01:49AM -0400, Bill Gray wrote:
> Hi Cliff, and others,
>
> I'm seeking support for us to rework numastat over the next few months
> to add functionality to show more per NUMA node statistics for
> applications and the general system. See the attached program for
> examples of the functionality I hope to add. Andi seems OK with it, so
> long as we make the default behavior the same as today's script, which
> we will certainly try to do.
>
> What do people think? Please advise. Thanks!
That's fine with me too.
If you send the patches I'll apply them.
-Cliff
>
>
> -------- Original Message --------
> Subject: RE: numastat tool
> Date: Mon, 4 Jun 2012 20:45:05 +0000
> From: Kleen, Andi <andi.kleen@intel.com>
> To: Bill Gray <bgray@redhat.com>
> CC: Douglas Shakshober <dshaks@redhat.com>, Bill Burns <bburns@redhat.com>
>
>
> >Assuming we make the default behavior the same, etc., what would you
> >think about us "enhancing" numastat to also include the functionality of
> >the attached code?
>
> Sounds good. It's just a perl script today, but turning it into C should
> be fine.
> And better information is always good. Just default should stay to same
> to not break scripts.
>
> I don't maintain numactl anymore though, so you would need to ask
> Cliff Wickman / linux-numa@vger.kernel.org
>
> -Andi
>
>
> -------- Original Message --------
> Subject: numastat tool
> Date: Mon, 04 Jun 2012 16:36:23 -0400
> From: Bill Gray <bgray@redhat.com>
> To: Kleen, Andi <andi.kleen@intel.com>
> CC: Douglas Shakshober <dshaks@redhat.com>, Bill Burns <bburns@redhat.com>
>
>
> Hi Andi,
>
> We're working on a tool to show per-node usage of memory for
> applications and the system as a whole. We're hoping to get the tool in
> RHEL 6.4. See the attached source code -- try it out if you like.
> Basically, it adds up /proc info and displays it on a per-node basis.
>
> The tool is currently called "nmstat", but "numastat" is such a better
> name.... ;-)
>
> Assuming we make the default behavior the same, etc., what would you
> think about us "enhancing" numastat to also include the functionality of
> the attached code?
>
> Please let us know your thoughts. Thank you!
>
> - Bill
>
>
>
> ---------- following output demonstrates nmstat showing effectiveness of
> numad moving some "pig" test processes.... Hopefully email won't
> horribly fold the lines ------------
>
>
> # ./nmstat -V
> ./nmstat version: 20120412: Jun 4 2012
>
>
> # ./nmstat 1
>
> Per-node process memory usage (in MBs) for PID 1 (init):
> Node 0 Node 1 Node 2
> Node 3 Node 4 Node 5 Node 6 Node 7
> ---------- ---------- ----------
> ---------- ---------- ---------- ---------- ----------
> huge 0.00 0.00 0.00
> 0.00 0.00 0.00 0.00 0.00
> heap 0.02 0.00 0.01
> 0.00 0.10 0.00 0.01 0.02
> stack 0.00 0.00 0.00
> 0.00 0.01 0.00 0.00 0.00
> private 0.70 0.00 0.00
> 0.01 0.63 0.00 0.00 0.02
> total 0.72 0.00 0.01
> 0.01 0.74 0.00 0.01 0.04
>
>
> # ./nmstat -n pig
>
> Per-node process memory usage (in MBs):
> PID Node 0 Node 1 Node 2
> Node 3 Node 4 Node 5 Node 6 Node 7
> ---------- ---------- ---------- ----------
> ---------- ---------- ---------- ---------- ----------
> 22075 (pig) 0.08 2998.34 0.00
> 0.00 0.10 0.00 1002.20 1000.00
> 22076 (pig) 1000.04 0.11 2001.92
> 0.00 998.19 0.00 1000.13 0.00
> 22077 (pig) 0.04 1001.96 0.00
> 2000.07 998.19 0.00 0.13 1000.00
> 22078 (pig) 1000.04 0.11 2003.71
> 998.15 0.09 1000.00 0.13 0.00
> 22079 (pig) 1001.89 0.14 0.00
> 0.00 1000.04 2001.89 0.13 998.15
>
>
> # numad -S 0 -p 22075 -p 22076 -p 22077 { this tool moves processes
> around for NUMA affinity }
>
> ... a minute passes ....
>
> # numad -i0 { turn off numad }
>
>
> # ./nmstat -n pig
>
> Per-node process memory usage (in MBs):
> PID Node 0 Node 1 Node 2
> Node 3 Node 4 Node 5 Node 6 Node 7
> ---------- ---------- ---------- ----------
> ---------- ---------- ---------- ---------- ----------
> 22075 (pig) 0.00 5000.43 0.00
> 0.00 0.00 0.00 0.29 0.00
> 22076 (pig) 0.00 0.00 0.00
> 0.00 0.00 0.00 5000.39 0.00
> 22077 (pig) 0.00 0.00 0.00
> 5000.10 0.00 0.00 0.29 0.00
> 22078 (pig) 1000.00 0.03 2003.71
> 998.15 0.06 1000.00 0.29 0.00
> 22079 (pig) 1001.86 0.05 0.00
> 0.00 1000.00 2001.89 0.29 998.15
>
>
>
> /*
>
> nmstat - NUMA monitoring tool to show per-node usage of memory
> Copyright (C) 2012 Bill Gray (bgray@redhat.com), Red Hat Inc
>
> nmstat is free software; you can redistribute it and/or modify it under the
> terms of the GNU Lesser General Public License as published by the Free
> Software Foundation; version 2.1.
>
> nmstat 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 Lesser General Public License for more
> details.
>
> You should find a copy of v2.1 of the GNU Lesser General Public License
> somewhere on your Linux system; if not, write to the Free Software Foundation,
> Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>
> */
>
>
> // Compile with: gcc -g -std=gnu99 -o nmstat nmstat.c
>
>
>
> #define __USE_MISC
> #include <ctype.h>
> #include <dirent.h>
> #include <errno.h>
> #include <getopt.h>
> #include <stdint.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/types.h>
> #include <unistd.h>
>
>
> #define BUF_SIZE 2048
> #define KILOBYTE (1024)
> #define MEGABYTE (1024 * 1024)
>
>
> // Structure to accumulate memory info from /proc/<PID>/numa_maps for a
> // specific process, or from /sys/devices/system/node/node?/meminfo for
> // system-wide data. Tables are defined below for each process and for
> // system-wide data.
> #define MAX_NODES 128
> typedef struct meminfo {
> int index;
> int seen;
> char *token;
> char *label;
> double node[MAX_NODES];
> } meminfo_t, *meminfo_p;
>
>
> #define PROC_HUGE_INDEX 0
> #define PROC_PRIVATE_INDEX 3
> #define PROC_TOTAL_INDEX 4
>
>
> meminfo_t proc_table[] = {
> { 0, 0, "huge", "huge" },
> { 1, 0, "heap", "heap" },
> { 2, 0, "stack", "stack" },
> { 3, 0, "N", "private" },
> { 4, 0, "total", "total" }
> };
> #define PROC_TABLE_SIZE (sizeof(proc_table) / sizeof(proc_table[0]))
>
>
> meminfo_t sys_table[] = {
> { 0, 0, "MemTotal", "MemTotal" },
> { 1, 0, "MemFree", "MemFree" },
> { 2, 0, "MemUsed", "MemUsed" },
> { 3, 0, "Active", "Active" },
> { 4, 0, "Inactive", "Inactive" },
> { 5, 0, "HighTotal", "HighTotal" },
> { 6, 0, "HighFree", "HighFree" },
> { 7, 0, "LowTotal", "LowTotal" },
> { 8, 0, "LowFree", "LowFree" },
> { 9, 0, "Active(anon)", "Active(anon)" },
> { 10, 0, "Inactive(anon)", "Inactive(anon)" },
> { 11, 0, "Active(file)", "Active(file)" },
> { 12, 0, "Inactive(file)", "Inactive(file)" },
> { 13, 0, "Unevictable", "Unevictable" },
> { 14, 0, "Mlocked", "Mlocked" },
> { 15, 0, "Dirty", "Dirty" },
> { 16, 0, "Writeback", "Writeback" },
> { 17, 0, "FilePages", "FilePages" },
> { 18, 0, "Mapped", "Mapped" },
> { 19, 0, "AnonPages", "AnonPages" },
> { 20, 0, "Shmem", "Shmem" },
> { 21, 0, "KernelStack", "KernelStack" },
> { 22, 0, "PageTables", "PageTables" },
> { 23, 0, "NFS_Unstable", "NFS_Unstable" },
> { 24, 0, "Bounce", "Bounce" },
> { 25, 0, "WritebackTmp", "WritebackTmp" },
> { 26, 0, "Slab", "Slab" },
> { 27, 0, "SReclaimable", "SReclaimable" },
> { 28, 0, "SUnreclaim", "SUnreclaim" },
> { 29, 0, "AnonHugePages", "AnonHugePages" },
> { 30, 0, "HugePages_Total", "HugePages_Total" },
> { 31, 0, "HugePages_Free", "HugePages_Free" },
> { 32, 0, "HugePages_Surp", "HugePages_Surp" }
> };
> #define SYS_TABLE_SIZE (sizeof(sys_table) / sizeof(sys_table[0]))
>
>
> meminfo_t numastat_table[] = {
> { 0, 0, "numa_hit", "Numa_Hit" },
> { 1, 0, "numa_miss", "Numa_Miss" },
> { 2, 0, "numa_foreign", "Numa_Foreign" },
> { 3, 0, "interleave_hit", "Interleave_Hit" },
> { 4, 0, "local_node", "Local_Node" },
> { 5, 0, "other_node", "Other_Node" },
> };
> #define NUMASTAT_TABLE_SIZE (sizeof(numastat_table) / sizeof(numastat_table[0]))
>
>
> // To allow re-ordering the memory categories in sys_table and numastat_table,
> // a simple hash index is used to look up the categories. The hash table size
> // should be bigger than necessary to reduce collisions (and because these hash
> // algorithms depend on unused buckets.
> #define HASH_TABLE_SIZE 151
> struct hash_entry {
> char *name;
> int index;
> } hash_table[HASH_TABLE_SIZE];
>
>
> int verbose = 0;
> int num_nodes = 0;
> int show_the_system_info = 0;
> int print_header = 1;
> int hash_collisions = 0;
> char *pattern = NULL;
> char *prog_name = NULL;
> double page_size_in_bytes = 0;
> double huge_page_size_in_bytes = 0;
>
>
> void print_usage_and_exit() {
> fprintf(stderr, "Usage: %s [-v] [-V] [-n <pattern>] [-s] [ PID... ]\n", prog_name);
> fprintf(stderr, "-v makes some reports more verbose\n");
> fprintf(stderr, "-V shows the code version\n");
> fprintf(stderr, "-n <pattern> looks up PIDs with <pattern> in command\n");
> fprintf(stderr, "-s shows some system wide memory usage\n");
> exit(EXIT_FAILURE);
> }
>
>
> void print_version() {
> char *version_string = "20120412";
> printf("%s version: %s: %s\n", prog_name, version_string, __DATE__);
> exit(EXIT_SUCCESS);
> }
>
>
> char *command_name_for_pid(int pid) {
> // get command name field from status file
> char fname[128];
> static char buf[BUF_SIZE];
> snprintf(fname, sizeof(fname), "/proc/%d/status", pid);
> FILE *fs = fopen(fname, "r");
> if (!fs) {
> return NULL;
> } else {
> while (fgets(buf, BUF_SIZE, fs)) {
> if (strstr(buf, "Name:") == buf) {
> fclose(fs);
> char *p = &buf[5];
> while (isspace(*p)) {
> p++;
> }
> if (p[strlen(p) - 1] == '\n') {
> p[strlen(p) - 1] = '\0';
> }
> return p;
> }
> }
> fclose(fs);
> }
> return NULL;
> }
>
>
> void show_process_info(int pid, int show_sub_categories) {
> // Zero per-node page counts for all memory categories
> for (int ix = 0; (ix < PROC_TABLE_SIZE); ix++) {
> memset(&proc_table[ix].node, 0, sizeof(proc_table[ix].node));
> }
> // Open numa_map for this PID
> char fname[128];
> snprintf(fname, sizeof(fname), "/proc/%d/numa_maps", pid);
> FILE *fs = fopen(fname, "r");
> if (!fs) {
> fprintf(stderr, "Can't find numa_map for pid %d\n", pid);
> return;
> }
> // Add up sub-category memory used from each node. Must go line by line
> // through the numa_map figuring out which category memory, node, and the
> // amount.
> char buf[BUF_SIZE];
> while (fgets(buf, BUF_SIZE, fs)) {
> int category = PROC_PRIVATE_INDEX; // init category to the catch-all...
> const char *delimiters = " \t\r\n";
> char *p = strtok(buf, delimiters);
> while (p) {
> // If the memory category for this line is still the catch-all
> // (i.e. private), then see if the current token is a special
> // keyword for a specific memory sub-category.
> if (category == PROC_PRIVATE_INDEX) {
> for (int ix = 0; (ix < PROC_PRIVATE_INDEX); ix++) {
> if (!strncmp(p, proc_table[ix].token, strlen(proc_table[ix].token))) {
> category = ix;
> break;
> }
> }
> }
> // If the current token is a per-node pages quantity, parse the
> // node number and accumulate the number of pages in the specific
> // category (and also add to the total).
> if (p[0] == 'N') {
> int node_num = (int)strtol(&p[1], &p, 10);
> if (p[0] != '=') {
> perror("node value parse error");
> exit(EXIT_FAILURE);
> }
> long pages = strtol(&p[1], &p, 10);
> double MBs; // init MBs with proper page size multiplier
> if (category == PROC_HUGE_INDEX) {
> MBs = huge_page_size_in_bytes;
> } else {
> MBs = page_size_in_bytes;
> }
> MBs *= (double)pages;
> MBs /= (double)MEGABYTE;
> proc_table[category].node[node_num] += MBs;
> proc_table[PROC_TOTAL_INDEX].node[node_num] += MBs;
> }
> // Get next token on the line
> p = strtok(NULL, delimiters);
> }
> }
> fclose(fs);
> // Print header information for the process(es). Vary some stuff according
> // to number of processes and detail being shown.
> if (print_header) {
> print_header = show_sub_categories; // Don't reprint header unless showing detail
> printf("\nPer-node process memory usage (in MBs)");
> if (show_sub_categories) {
> printf(" for PID %d (%s)", pid, command_name_for_pid(pid));
> }
> printf(":\n", pid);
> // Print proc_table header
> if (show_sub_categories) {
> printf("%15s", " ");
> } else {
> printf("%15s", "PID ");
> }
> for (int ix = 0; (ix < num_nodes); ix++) {
> char node_label[32];
> snprintf(node_label, sizeof(node_label), "Node %d", ix);
> printf("%15s", node_label);
> }
> printf("\n");
> if (show_sub_categories) {
> printf("%15s", " ");
> } else {
> printf("%15s", "----------");
> }
> for (int ix = 0; (ix < num_nodes); ix++) {
> printf("%15s", "----------");
> }
> printf("\n");
> }
> // Print the process memory info -- optionally showing the sub-categories
> for (int ix = (show_sub_categories ? 0 : PROC_TOTAL_INDEX); (ix <= PROC_TOTAL_INDEX); ix++) {
> if (show_sub_categories || (ix != PROC_TOTAL_INDEX)) {
> printf("%15s", proc_table[ix].label);
> } else {
> char tmp_buf[64];
> snprintf(tmp_buf, 15, "%d (%s)", pid, command_name_for_pid(pid));
> printf("%15s", tmp_buf);
> }
> for (int node_ix = 0; (node_ix < num_nodes); node_ix++) {
> printf("%15.2f", proc_table[ix].node[node_ix]);
> }
> printf("\n");
> }
> } // show_process_info
>
>
> int hash_ix(char *s) {
> int c;
> unsigned int h = 17;
> while (c = *s++) {
> // h * 33 + c
> h = ((h << 5) + h) + c;
> }
> return (h % HASH_TABLE_SIZE);
> }
>
>
> int hash_lookup(char *s) {
> int ix = hash_ix(s);
> while (hash_table[ix].name) { // Assumes big table with blank entries
> if (!strcmp(s, hash_table[ix].name)) {
> return hash_table[ix].index; // found it
> }
> ix += 1;
> if (ix >= HASH_TABLE_SIZE) {
> ix = 0;
> }
> }
> return -1;
> }
>
>
> int hash_insert(char *s, int i) {
> int ix = hash_ix(s);
> while (hash_table[ix].name) { // assumes no duplicate entries
> hash_collisions += 1;
> ix += 1;
> if (ix >= HASH_TABLE_SIZE) {
> ix = 0;
> }
> }
> hash_table[ix].name = s;
> hash_table[ix].index = i;
> return ix;
> }
>
>
> void show_numastat_info() {
> // Build numastat_table hash index and zero the numastat_table per-node values
> memset(hash_table, 0, sizeof(hash_table));
> for (int ix = 0; (ix < NUMASTAT_TABLE_SIZE); ix++) {
> hash_insert(numastat_table[ix].token, numastat_table[ix].index);
> memset(&numastat_table[ix].node, 0, sizeof(numastat_table[ix].node));
> }
> // printf("There are %d numastat table hash collisions.\n", hash_collisions);
> // Open /sys/devices/system/node/node?/numastat for each node and store data in numastat_table.
> for (int node_num = 0; (node_num < num_nodes); node_num++) {
> char buf[BUF_SIZE];
> char fname[128];
> snprintf(fname, sizeof(fname), "/sys/devices/system/node/node%d/numastat", node_num);
> FILE *fs = fopen(fname, "r");
> if (!fs) {
> perror(fname);
> exit(EXIT_FAILURE);
> }
> while (fgets(buf, BUF_SIZE, fs)) {
> char *tok[32];
> int tokens = 0;
> const char *delimiters = " \t\r\n:";
> char *p = strtok(buf, delimiters);
> if (p == NULL) {
> continue; // Skip blank lines;
> }
> while (p) {
> tok[tokens++] = p;
> p = strtok(NULL, delimiters);
> // Code below assumes >= 2 tokens
> // example line: "numa_miss 16463"
> }
> int index = hash_lookup(tok[0]);
> if (index >= 0) {
> double MBs = page_size_in_bytes;
> MBs *= (double)atol(tok[1]);
> MBs /= (double)MEGABYTE;
> numastat_table[index].seen = 1;
> numastat_table[index].node[node_num] += MBs;
> } else {
> printf("Token %s not in hash table.\n", tok[0]);
> }
> }
> fclose(fs);
> }
> // Print numastat_table header
> printf("\nPer-node numastat info (in MBs):\n");
> printf("%15s", " ");
> for (int ix = 0; (ix < num_nodes); ix++) {
> char node_label[32];
> snprintf(node_label, sizeof(node_label), "Node %d", ix);
> printf("%15s", node_label);
> }
> printf("\n");
> printf("%15s", " ");
> for (int ix = 0; (ix < num_nodes); ix++) {
> printf("%15s", "----------");
> }
> printf("\n");
> // Print the numastat memory data
> for (int ix = 0; (ix < NUMASTAT_TABLE_SIZE); ix++) {
> if (!numastat_table[ix].seen) {
> continue;
> }
> printf("%15s", numastat_table[ix].label);
> for (int node_ix = 0; (node_ix < num_nodes); node_ix++) {
> printf("%15.2f", numastat_table[ix].node[node_ix]);
> }
> printf("\n");
> }
> } // show_numastat_info
>
>
> void show_system_info() {
> // Build sys_table hash index and zero the sys_table per-node values
> memset(hash_table, 0, sizeof(hash_table));
> for (int ix = 0; (ix < SYS_TABLE_SIZE); ix++) {
> hash_insert(sys_table[ix].token, sys_table[ix].index);
> memset(&sys_table[ix].node, 0, sizeof(sys_table[ix].node));
> }
> // printf("There are %d sys_table hash collisions.\n", hash_collisions);
> // Open /sys/devices/system/node/node?/meminfo for each node and store data in sys_table.
> for (int node_num = 0; (node_num < num_nodes); node_num++) {
> char buf[BUF_SIZE];
> char fname[128];
> snprintf(fname, sizeof(fname), "/sys/devices/system/node/node%d/meminfo", node_num);
> FILE *fs = fopen(fname, "r");
> if (!fs) {
> perror(fname);
> exit(EXIT_FAILURE);
> }
> while (fgets(buf, BUF_SIZE, fs)) {
> char *tok[32];
> int tokens = 0;
> const char *delimiters = " \t\r\n:";
> char *p = strtok(buf, delimiters);
> if (p == NULL) {
> continue; // Skip blank lines;
> }
> while (p) {
> tok[tokens++] = p;
> p = strtok(NULL, delimiters);
> // Code below assumes >= 4 tokens
> // example line: "Node 3 Inactive: 210680 kB"
> }
> int index = hash_lookup(tok[2]);
> if (index >= 0) {
> double MBs;
> // start with the right value multiplier
> if (!strncmp("HugePages", tok[2], 9)) {
> MBs = huge_page_size_in_bytes;
> } else {
> MBs = KILOBYTE;
> }
> MBs *= (double)atol(tok[3]);
> MBs /= (double)MEGABYTE;
> sys_table[index].seen = 1;
> sys_table[index].node[node_num] += MBs;
> } else {
> printf("Token %s not in hash table.\n", tok[2]);
> }
> }
> fclose(fs);
> }
> // Print sys_table header
> printf("\nPer-node system memory usage (in MBs):\n");
> printf("%15s", " ");
> for (int ix = 0; (ix < num_nodes); ix++) {
> char node_label[32];
> snprintf(node_label, sizeof(node_label), "Node %d", ix);
> printf("%15s", node_label);
> }
> printf("\n");
> printf("%15s", " ");
> for (int ix = 0; (ix < num_nodes); ix++) {
> printf("%15s", "----------");
> }
> printf("\n");
> // Print the system memory data
> for (int ix = 0; (ix < SYS_TABLE_SIZE); ix++) {
> if (!sys_table[ix].seen) {
> continue;
> }
> printf("%15s", sys_table[ix].label);
> for (int node_ix = 0; (node_ix < num_nodes); node_ix++) {
> printf("%15.2f", sys_table[ix].node[node_ix]);
> }
> printf("\n");
> }
> show_numastat_info();
> } // show_system_info
>
>
> static int starts_with_digit(const struct dirent *dptr) {
> return (isdigit(dptr->d_name[0]));
> }
>
>
> static int node_and_digits(const struct dirent *dptr) {
> char *p = (char *)(dptr->d_name);
> if (*p++ != 'n') return 0;
> if (*p++ != 'o') return 0;
> if (*p++ != 'd') return 0;
> if (*p++ != 'e') return 0;
> do {
> if (!isdigit(*p++)) return 0;
> } while (*p != '\0');
> return 1;
> }
>
>
> static int get_num_nodes() {
> // Count directory names of the form: /sys/devices/system/node/node<N>
> struct dirent **namelist;
> int files = scandir ("/sys/devices/system/node", &namelist, node_and_digits, NULL);
> if (files < 0) {
> perror ("Couldn't open /sys/devices/system/node");
> } else {
> return files;
> }
> }
>
>
> static double get_huge_page_size_in_bytes() {
> double huge_page_size = 0;;
> FILE *fs = fopen("/proc/meminfo", "r");
> if (!fs) {
> perror("Can't open /proc/meminfo");
> exit(EXIT_FAILURE);
> }
> char buf[BUF_SIZE];
> while (fgets(buf, BUF_SIZE, fs)) {
> if (!strncmp("Hugepagesize", buf, 12)) {
> char *p = &buf[12];
> while ((!isdigit(*p)) && (p < buf + BUF_SIZE)) {
> p++;
> }
> huge_page_size = strtod(p, NULL);
> break;
> }
> }
> fclose(fs);
> return huge_page_size * KILOBYTE;
> }
>
>
> void show_info_for_pids_from_pattern_search() {
> // Search all /proc/<PID>/cmdline files and /proc/<PID>/status:Name fields
> // for matching patterns. Show the memory details for matching PIDs.
> struct dirent **namelist;
> int files = scandir ("/proc", &namelist, starts_with_digit, NULL);
> if (files < 0) {
> perror ("Couldn't open /proc");
> }
> for (int ix = 0; (ix < files); ix++) {
> char buf[BUF_SIZE];
> // First get Name field from status file
> int pid = atoi(namelist[ix]->d_name);
> char *p = command_name_for_pid(pid);
> if (p) {
> strcpy(buf, p);
> } else {
> buf[0] = '\0';
> }
> // Next copy cmdline file contents onto end of buffer. Do it a
> // character at a time to convert nulls to spaces.
> char fname[128];
> snprintf(fname, sizeof(fname), "/proc/%s/cmdline", namelist[ix]->d_name);
> FILE *fs = fopen(fname, "r");
> if (fs) {
> p = buf;
> while (*p != '\0') {
> p++;
> }
> *p++ = ' ';
> int c;
> while (((c = fgetc(fs)) != EOF) && (p < buf + BUF_SIZE - 1)) {
> if (c == '\0') {
> c = ' ';
> }
> *p++ = c;
> }
> *p++ = '\0';
> fclose(fs);
> }
> if (strstr(buf, pattern)) {
> if (pid != getpid()) {
> show_process_info(pid, verbose);
> }
> }
> }
> }
>
>
> int main(int argc, char **argv) {
> prog_name = argv[0];
> // Figure out page sizes and number of nodes
> num_nodes = get_num_nodes();
> page_size_in_bytes = (double)sysconf(_SC_PAGESIZE);
> huge_page_size_in_bytes = get_huge_page_size_in_bytes();
> // Parse program options
> int opt;
> while ((opt = getopt(argc, argv, "n:svV")) != -1) {
> switch (opt) {
> case 'n': pattern = optarg; break;
> case 's': show_the_system_info = 1; break;
> case 'v': verbose = 1; break;
> case 'V': print_version(); break;
> default: print_usage_and_exit(); break;
> }
> }
> // If no PIDs specified, show the system-wide info
> if ((optind == argc) && (!pattern)) {
> show_the_system_info = 1;
> }
> // Print info for each specified PID
> int show_sub_categories = (verbose || ((argc - optind) == 1));
> while (optind < argc) {
> int pid = atoi(argv[optind++]);
> show_process_info(pid, show_sub_categories);
> }
> if (pattern) {
> show_info_for_pids_from_pattern_search();
> }
> // Print system-wide info
> if (show_the_system_info) {
> show_system_info();
> }
> exit(EXIT_SUCCESS);
> }
>
>
--
Cliff Wickman
SGI
cpw@sgi.com
(651) 683-3824
next prev parent reply other threads:[~2012-06-05 12:26 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <4FCD1C47.3010601@redhat.com>
2012-06-05 4:01 ` Red Hat would like to rework / enhance numastat Bill Gray
2012-06-05 12:26 ` Cliff Wickman [this message]
2012-08-21 22:33 ` Bill Gray
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20120605122631.GA28054@sgi.com \
--to=cpw@sgi.com \
--cc=andi@firstfloor.org \
--cc=bburns@redhat.com \
--cc=bgray@redhat.com \
--cc=dshaks@redhat.com \
--cc=linux-numa@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.