4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21 * CA 95054 USA or visit www.sun.com if you need additional information or
27 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Use is subject to license terms.
31 * This file is part of Lustre, http://www.lustre.org/
32 * Lustre is a trademark of Sun Microsystems, Inc.
34 * lustre/utils/ltrack_stats.c
36 * Author: Milind Dumbare <milind@clusterfs.com>
39 #include <sys/types.h>
50 #define TRACK_BY_GID 0
51 #define TRACK_BY_PPID 1
52 #define TRACK_BY_PID 2
53 #define TRACK_FOR_ALL 3
55 /* We can have at the most 1024 llstats monitoring 1024 lustre clients
57 #define NO_OF_CLIENTS 1024
59 /* length of absolute path of vfs_ops_stats */
60 #define LEN_STATS 1024
62 /* Length of llstat command with all its switches and command line options */
63 #define LEN_LLSTAT (25 + LEN_STATS)
65 /* strlen of each lustre client entry in /proc/fs/lustre/llite/ */
66 #define LEN_CLIENT 1024
68 /* size of output of llstat command we read at a time */
69 #define LLSTAT_READ_SIZE 1024
71 /* Length of command given on command line */
78 printf("ltrack_stats runs command given and does one of the "
80 "\t1. Writes its pid to "
81 "/proc/fs/lustre/llite/.../stats_track_pid\n"
82 " to collects stats for that process.\n"
83 "\t2. Writes its ppid to "
84 "/proc/fs/lustre/llite/.../stats_track_ppid\n"
85 " to collect stats of that process and all its children \n"
86 "\t3. Sets gid of process to some random gid (444) and also\n"
87 " writes that to/proc/fs/lustre/llite/.../stats_track_gid to"
88 " collect stats \nof all processes in that group\n\n"
89 " It also uses llstat to generate output with interval of 1 "
90 " second and duration\n of run of command for plot-llstat to "
91 "generate a graph\n\n");
92 printf("ltrack_stats [-l filename] [-g <gid> | -a | -i | -c | -h ]\n"
93 "\t<command with arguments ...>\n\n");
94 printf("-l: outputs the llstat.pl's output to given <filename>"
95 "with interval of 1 second \nbetween each output and flag for"
96 "graphable output. If -l flag is not given llstat \nwont be"
99 printf("-g: for displaying VFS operation statistics collected"
100 "for all processes having \ngroup id as given <gid> \n\n");
102 printf("-a: for displaying VFS operations statistics collected"
103 "for all processes\n\n");
105 printf("-i: for displaying VFS operation statistics collected"
106 "for only given <command's> \nPID.\n\n");
108 printf("-c: for displaying VFS operation statistics collected"
109 " for all processes whose \nparents' PID is same as pid of "
110 "<command> to be executed\n\n");
112 printf("-h: for showing this help\n");
115 /* - type: to which file data should be written to track VFS operations
117 * - id: we either need to write gid which is given on command line or pid
118 * to collect statistics of that process or its children. */
120 void write_track_xid(int type, unsigned short id, char* stats_path)
124 /* for loop is used if we have more than one lustre clients on same
125 * machine. glob() return /proc entry for each lustre client */
129 strcat(stats_path, "/stats_track_gid");
132 strcat(stats_path, "/stats_track_ppid");
135 strcat(stats_path, "/stats_track_pid");
139 fp = fopen(stats_path, "w+");
141 fprintf(stderr, "Error: Couldn't open /proc entry file: %s\n",
145 if (fprintf(fp, "%d", id) < 0) {
146 fprintf(stderr, "Error: Couldn't write id to tracking /proc"
147 "entry file: %s\n", stats_path);
151 fprintf(stderr, "Error: Couldn't close tracking /proc entry"
152 " file: %s\n", stats_path);
157 /* Get extra command lines concatenated to "command Function getopt scans
158 * one switch and its optional argument. So if command line is
159 * "-g 1234 make bzImage" 1234 is optarg and "make bzImage" will be collected
160 * with following function to exec it. */
161 char* get_command_from_argv(int optind, int argc, char** argv,char* optarg,
166 strcpy(command, optarg);
167 strcat(command, " ");
168 for (index = optind; index < argc; index++) {
169 strcat(command, argv[index]);
170 strcat(command, " ");
172 if (strlen(command) == 1) {
173 fprintf(stderr,"Error: command missing \n");
176 } else if (strlen(command) > COMM_LEN) {
177 fprintf(stderr,"Error: Too long command \n");
185 /* Check for the llstat command in $PATH env variable. */
190 status = system("which llstat.pl &> /dev/null");
192 fprintf(stderr,"Error: llstat.pl not found in PATH\n");
197 pid_t fork_llstat_command(char* llstat_file,char* stats_path)
199 char truncate_command[100];
200 char llstat_command[LEN_LLSTAT];
201 pid_t pid_llstat_command;
202 FILE *fp_popen, *fp_out;
203 char buffer[LLSTAT_READ_SIZE];
206 /* Truncating llstat output file as it will be opened in while
207 * loop to append output */
208 sprintf(truncate_command,"> %s",llstat_file);
209 if ((ret = system(truncate_command)) != 0) {
210 ret = WEXITSTATUS(ret);
211 printf("error excuting truncate command: %d\n", ret);
215 strcat(stats_path, "/stats");
217 /* creating a string of llstat command to give to
219 sprintf(llstat_command, "llstat -i 1 -g %s ",
222 /* fork for llstat */
223 if ((pid_llstat_command = fork()) < 0)
224 fprintf(stderr, "Error: Fork error\n");
226 /* in child (llstat) */
227 if (pid_llstat_command == 0) {
228 /* Doing popen for llstat command */
229 fp_popen = popen(llstat_command, "r");
230 if (fp_popen == NULL) {
231 fprintf(stderr,"Couldn't popen the llstat command:"
232 "\"%s\"n", llstat_command);
235 while (fgets(buffer, LLSTAT_READ_SIZE, fp_popen) != NULL) {
236 /* Following code should be in while loop as llstat
237 * will keep on sending output each second and will
238 * not exit on itself. It will be killed when we finsh
239 * with our command so we must make the output file
240 * consistent after writing each 1024 bytes chunk */
242 /* opening file where llstat will write its output */
243 fp_out = fopen(llstat_file, "a");
245 fprintf(stderr, "Error: Couldn't open llstat"
246 "outfile file: %s\n",
250 /* fgets reads the popen output and fprintf writes it to
253 if (fputs(buffer, fp_out) == EOF) {
254 fprintf(stderr, "Error: Couldn't write output"
255 "of llstat to out file\n");
259 /* closing file opened for storing llstat's output */
260 if (fclose(fp_out)) {
261 fprintf(stderr, "Error: Couldn't close llstat"
262 "outfile: %s\n", llstat_file);
266 /* closing popen for llstat */
267 if (pclose(fp_popen) < 0) {
268 fprintf(stderr, "Error: Couldn't pclos"
269 " llstat popen call\n");
273 return pid_llstat_command;
276 pid_t fork_actual_command(int type, unsigned short id, char* stats_path,
281 /* starting ltrack_stats functionality here */
282 if ((pid = fork()) < 0)
283 fprintf(stderr, "Error: Fork error\n");
285 /* fork for executing command */
289 if (setgid(id) < 0) {
290 fprintf(stderr, "Error: failed to"
302 /* 0 has to be written to vfs_track_pid to collect
303 * statistics of all processes */
309 write_track_xid(type, pid, stats_path);
310 execl("/bin/sh", "sh", "-c", command, (char *)0);
317 char* get_path_stats(int with_llstat, char* stats_path)
319 glob_t stats_glob_buffer;
324 /* No slots reserved in gl_pathv. Store the found path at 0 location */
325 stats_glob_buffer.gl_offs = 0;
327 /* doing glob() for attaching llstat to monitor each vfs_ops_stat for
328 * mulitiple lustre clients */
329 if (glob("/proc/fs/lustre/llite/*", GLOB_DOOFFS, NULL,
330 &stats_glob_buffer) != 0) {
331 fprintf(stderr,"Error: Couldn't find /proc entry for "
336 /* If multiple client entries found in /proc/fs/lustre/llite user will
337 * be prompted with choice of all */
338 if (stats_glob_buffer.gl_pathc > 1 && with_llstat) {
340 printf("Multiple lustre clients found, continuing... \n");
342 /* If flow is here again it means there was an error
343 * and notifying that to user */
346 if ((ret = system("clear")) != 0) {
347 ret = WEXITSTATUS(ret);
348 printf("error excuting clear command: %d\n", ret);
351 fprintf(stderr, "Error: Please give correct "
354 /* Simple menu based interface to avoid possible
355 * spelling mistakes */
356 printf("\t\tMenu.\n");
357 for (i = 0; i < stats_glob_buffer.gl_pathc; i++)
358 printf("\t\t%d. %s\n", i+1,
359 stats_glob_buffer.gl_pathv[i]);
361 printf("\nEnter the lustre client number you want to "
363 if (scanf(" %d", &choice) == EOF && ferror(stdin)) {
364 perror("reading from stdin");
368 } while (choice > stats_glob_buffer.gl_pathc || choice < 1);
369 strcpy(stats_path, stats_glob_buffer.gl_pathv[choice - 1]);
371 /*if only one client then simply copying the path from glob */
372 strcpy(stats_path, stats_glob_buffer.gl_pathv[0]);
374 /* this frees dynamically allocated space by glob() for storing found
376 globfree(&stats_glob_buffer);
381 /* Writes the id (gid/ pid/ ppid) value in appropriate tracking proc entry file
382 * and EXECs the command given */
383 void fork_command(int type, unsigned short id, char* command, char* llstat_file)
385 pid_t pid_actual_command = 0;
386 pid_t pid_llstat_command = 0;
391 char stats_path[1024];
392 char stats_path_temp[1024];
394 if (strlen(llstat_file) == 0)
397 get_path_stats(with_llstat, stats_path);
398 strcpy(stats_path_temp, stats_path);
400 /* llstat process attached to monitor given command */
402 pid_llstat_command = fork_llstat_command(llstat_file,
405 /* forking a process which will exec command given */
406 pid_actual_command = fork_actual_command(type, id, stats_path,
409 if (waitpid(pid_actual_command, NULL, 0) != pid_actual_command)
410 fprintf(stderr, "Error: waitpid error\n");
413 /* comment #25 of BUG 10968 */
416 /* sending kill to all llstat commands created for each
417 * lustre-client respectively */
418 kill(pid_llstat_command, 9);
419 waitpid(pid_llstat_command, &status, 0);
421 /* if llstat child is killed by KILL only then print note for
422 * plotting graph and if its exited normally with errornous
423 * status then it means there were some error and llstat was
425 if (!WIFEXITED(status))
426 printf("\n\t[Note: Do \"$plot-llstat %s\" to plot a graph"
427 " using GNU plot]\n", llstat_file);
433 int main(int argc, char **argv)
435 char gid_string[5] = "";
438 char command[COMM_LEN] = "";
439 char llstat_file[100] = "";
441 /* Checking for root*/
443 fprintf(stderr, "Error: You need to be root\n");
448 /* Parsing command line switches */
449 while ((c = getopt(argc, argv, "l:g:c:i:a:h")) != 1)
452 if (strlen(optarg) > sizeof(llstat_file)-1) {
453 fprintf(stderr, "length of outfile file"
457 strncpy(llstat_file, optarg,
458 sizeof(llstat_file));
461 /* When any value is written to vfs_track_gid, then VFS
462 * operation statistics are collected for all
463 * processes of that group ID.
464 * write_track_xid writes given <gid> in vfs_track_gid
467 if (strlen(optarg) > sizeof(gid_string)-1)
469 strncpy(gid_string, optarg, sizeof(gid_string));
470 get_command_from_argv(optind, argc, argv, "",
472 gid = atoi(gid_string);
474 fork_command(TRACK_BY_GID, gid, command,
478 /* When any value is written to vfs_track_ppid, then VFS
479 * operation statistics are collected for all processes
480 * whose parents' PID is same as track_ppid.
481 *- write_track_xid writes pid to vfs_track_ppid here */
483 get_command_from_argv(optind, argc, argv,
485 fork_command(TRACK_BY_PPID, 0, command,
489 /* When a non-zero value is written to vfs_track_pid,
490 * then VFS operation statistics are collected for only
491 * that PID.Write_track_xid writes pid to vfs_track_pid
492 * here.Wei need to FORK a new process and EXEC it with
493 * given <command>. */
495 get_command_from_argv(optind, argc, argv,
497 fork_command(TRACK_BY_PID, 0, command,
501 /* When VFS operation statistics for all processes are
502 * to be collected, "0" is written to vfs_track_pid. */
504 get_command_from_argv(optind, argc, argv,
506 fork_command(TRACK_FOR_ALL, 0, command,