1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2008-2009, Intel Corporation. 23 * All Rights Reserved. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <memory.h> 29 #include <string.h> 30 #include <procfs.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include <limits.h> 35 #include <unistd.h> 36 37 #include "latencytop.h" 38 39 static GHashTable *proc_table = NULL; /* pid -> char * */ 40 static GHashTable *klog_table = NULL; /* char * -> uint64_t total */ 41 static char klog_filename[PATH_MAX] = DEFAULT_KLOG_FILE; 42 static int klog_level = LT_KLOG_LEVEL_NONE; 43 44 static void 45 print_proc(void *key, const char *args, FILE *fp) 46 { 47 pid_t pid = LT_POINTER_TO_INT(key); 48 char tmp[16]; 49 50 (void) snprintf(tmp, sizeof (tmp), "%ld,", (long)pid); 51 (void) fprintf(fp, "%-8s \"%s\"\n", tmp, args); 52 } 53 54 static void 55 print_stat(const char *key, lt_stat_data_t *log, FILE *fp) 56 { 57 (void) fprintf(fp, "%lld, %lld, %lld, %s\n", 58 (long long)log->total, 59 (long long)log->count, 60 (long long)log->max, 61 key); 62 } 63 64 /* 65 * Initialize kernel stack logging. 66 */ 67 void 68 lt_klog_init(void) 69 { 70 if (klog_table != NULL || proc_table != NULL) { 71 return; 72 } 73 74 klog_table = g_hash_table_new_full(g_str_hash, g_str_equal, 75 (GDestroyNotify)free, (GDestroyNotify)free); 76 lt_check_null(klog_table); 77 78 proc_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, 79 NULL, (GDestroyNotify)free); 80 lt_check_null(proc_table); 81 } 82 83 /* 84 * Set log file path. 85 */ 86 int 87 lt_klog_set_log_file(const char *filename) 88 { 89 FILE *fp; 90 int file_exist; 91 92 if (strlen(filename) >= sizeof (klog_filename)) { 93 return (-1); 94 } 95 96 file_exist = lt_file_exist(filename); 97 /* Test if we can write to the file */ 98 fp = fopen(filename, "a"); 99 if (fp == NULL) { 100 return (-2); 101 } 102 (void) fclose(fp); 103 /* Don't leave empty file behind */ 104 if (!file_exist) { 105 (void) unlink(filename); 106 } 107 108 (void) strncpy(klog_filename, filename, 109 sizeof (klog_filename)); 110 111 return (0); 112 } 113 114 /* 115 * Set log level. 116 */ 117 int 118 lt_klog_set_log_level(int level) 119 { 120 if (level < 0 || level > (int)LT_KLOG_LEVEL_ALL) { 121 return (-1); 122 } 123 124 klog_level = level; 125 126 return (0); 127 } 128 129 /* 130 * Write the log to file. 131 */ 132 void 133 lt_klog_write(void) 134 { 135 FILE *fp; 136 char buffer[32]; 137 138 if (klog_level == LT_KLOG_LEVEL_NONE) { 139 return; 140 } 141 142 g_assert(klog_table != NULL && proc_table != NULL); 143 144 fp = fopen(klog_filename, "a"); 145 if (fp == NULL) { 146 return; 147 } 148 149 lt_time_str(buffer, sizeof (buffer)); 150 151 (void) fprintf(fp, "# Log generated %s by %s\n", buffer, TITLE); 152 (void) fprintf(fp, "# List of processes\n"); 153 (void) fprintf(fp, "PID, CMD\n"); 154 g_hash_table_foreach(proc_table, 155 (GHFunc)print_proc, fp); 156 157 (void) fprintf(fp, "# Statistics\n"); 158 (void) fprintf(fp, "TOTAL, COUNT, MAX, PID, KSTACK\n"); 159 g_hash_table_foreach(klog_table, 160 (GHFunc)print_stat, fp); 161 162 (void) fclose(fp); 163 } 164 165 /* 166 * Clean up function. This will cause all log in memory be written to the 167 * log file. 168 */ 169 void 170 lt_klog_deinit(void) 171 { 172 if (klog_table != NULL) { 173 g_hash_table_destroy(klog_table); 174 klog_table = NULL; 175 } 176 177 if (proc_table != NULL) { 178 g_hash_table_destroy(proc_table); 179 proc_table = NULL; 180 } 181 } 182 183 /* 184 * Log a stack and its statistics. Only "total" will be logged, others are 185 * internally discarded. 186 */ 187 /* ARGSUSED */ 188 void 189 lt_klog_log(int level, pid_t pid, char *stack, 190 lt_stat_type_t type, uint64_t value) 191 { 192 lt_stat_data_t *entry = NULL; 193 char *psargs; 194 char *str; 195 int str_len; 196 197 if ((level & klog_level) == 0) { 198 return; 199 } 200 g_assert(klog_table != NULL && proc_table != NULL); 201 202 psargs = (char *)g_hash_table_lookup(proc_table, 203 LT_INT_TO_POINTER(pid)); 204 if (psargs == NULL) { 205 psargs = lt_get_proc_field(pid, LT_FIELD_PSARGS); 206 if (psargs == NULL) { 207 psargs = lt_get_proc_field(pid, LT_FIELD_FNAME); 208 } 209 210 if (psargs == NULL) { 211 return; 212 } 213 214 g_hash_table_insert(proc_table, 215 LT_INT_TO_POINTER(pid), psargs); 216 } 217 218 str_len = strlen(stack) + 20; 219 str = lt_malloc(str_len); 220 (void) snprintf(str, str_len, "%ld, \"%s\"", pid, stack); 221 222 entry = (lt_stat_data_t *)g_hash_table_lookup(klog_table, str); 223 if (entry == NULL) { 224 entry = (lt_stat_data_t *)lt_zalloc(sizeof (lt_stat_data_t)); 225 g_hash_table_insert(klog_table, str, entry); 226 } else { 227 free(str); 228 } 229 230 lt_update_stat_value(entry, type, value); 231 } 232