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 (void) fprintf(fp, "%-8ld \"%s\"\n", (long)key, args); 48 } 49 50 static void 51 print_stat(const char *key, lt_stat_data_t *log, FILE *fp) 52 { 53 (void) fprintf(fp, "%lld, %lld, %lld, %s\n", 54 (long long)log->lt_s_total, 55 (long long)log->lt_s_count, 56 (long long)log->lt_s_max, 57 key); 58 } 59 60 /* 61 * Initialization for kernel logging. 62 */ 63 void 64 lt_klog_init(void) 65 { 66 if (klog_table != NULL || proc_table != NULL) { 67 return; 68 } 69 70 klog_table = g_hash_table_new_full(g_str_hash, g_str_equal, 71 (GDestroyNotify)free, (GDestroyNotify)free); 72 lt_check_null(klog_table); 73 74 proc_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, 75 NULL, (GDestroyNotify)free); 76 lt_check_null(proc_table); 77 } 78 79 /* 80 * Set log file path. 81 */ 82 int 83 lt_klog_set_log_file(const char *filename) 84 { 85 FILE *fp; 86 int file_exist; 87 88 g_assert(strlen(filename) < sizeof (klog_filename)); 89 90 file_exist = lt_file_exist(filename); 91 /* Test if we can write to the file */ 92 fp = fopen(filename, "a"); 93 94 if (fp == NULL) { 95 return (-2); 96 } 97 98 (void) fclose(fp); 99 /* Don't leave empty file around */ 100 if (!file_exist) { 101 (void) unlink(filename); 102 } 103 104 (void) strncpy(klog_filename, filename, 105 sizeof (klog_filename)); 106 107 return (0); 108 } 109 110 /* 111 * Set log level. 112 */ 113 int 114 lt_klog_set_log_level(int level) 115 { 116 if (level < 0 || level > (int)LT_KLOG_LEVEL_ALL) { 117 return (-1); 118 } 119 120 klog_level = level; 121 122 return (0); 123 } 124 125 /* 126 * Write content to log file. 127 */ 128 void 129 lt_klog_write(void) 130 { 131 FILE *fp; 132 char buffer[32]; 133 134 if (klog_level == LT_KLOG_LEVEL_NONE) { 135 return; 136 } 137 138 g_assert(klog_table != NULL && proc_table != NULL); 139 fp = fopen(klog_filename, "a"); 140 141 if (fp == NULL) { 142 return; 143 } 144 145 lt_time_str(buffer, sizeof (buffer)); 146 147 (void) fprintf(fp, "# Log generated at %s by %s\n", buffer, TITLE); 148 (void) fprintf(fp, "# List of processes\n"); 149 (void) fprintf(fp, "PID, CMD\n"); 150 g_hash_table_foreach(proc_table, (GHFunc)print_proc, fp); 151 152 (void) fprintf(fp, "# Statistics\n"); 153 (void) fprintf(fp, "TOTAL, COUNT, MAX, PID, KSTACK\n"); 154 g_hash_table_foreach(klog_table, (GHFunc)print_stat, fp); 155 156 (void) fclose(fp); 157 } 158 159 /* 160 * Clean up. It flushes all log content in memory to log file. 161 */ 162 void 163 lt_klog_deinit(void) 164 { 165 if (klog_table != NULL) { 166 g_hash_table_destroy(klog_table); 167 klog_table = NULL; 168 } 169 170 if (proc_table != NULL) { 171 g_hash_table_destroy(proc_table); 172 proc_table = NULL; 173 } 174 } 175 176 /* 177 * Write a kernel stack and its statistics to log file. Only "total" will 178 * be logged, others are internally discarded. 179 */ 180 /* ARGSUSED */ 181 void 182 lt_klog_log(int level, pid_t pid, char *stack, 183 lt_stat_type_t type, uint64_t value) 184 { 185 lt_stat_data_t *entry = NULL; 186 char *psargs; 187 char *str; 188 int str_len; 189 190 if ((level & klog_level) == 0) { 191 return; 192 } 193 194 g_assert(klog_table != NULL && proc_table != NULL); 195 psargs = (char *)g_hash_table_lookup(proc_table, 196 LT_INT_TO_POINTER(pid)); 197 198 if (psargs == NULL) { 199 psargs = lt_get_proc_field(pid, LT_FIELD_PSARGS); 200 201 if (psargs == NULL) { 202 psargs = lt_get_proc_field(pid, LT_FIELD_FNAME); 203 } 204 205 if (psargs == NULL) { 206 return; 207 } 208 209 g_hash_table_insert(proc_table, 210 LT_INT_TO_POINTER(pid), psargs); 211 } 212 213 str_len = strlen(stack) + 20; 214 str = lt_malloc(str_len); 215 (void) snprintf(str, str_len, "%ld, \"%s\"", pid, stack); 216 entry = (lt_stat_data_t *)g_hash_table_lookup(klog_table, str); 217 218 if (entry == NULL) { 219 entry = (lt_stat_data_t *)lt_zalloc(sizeof (lt_stat_data_t)); 220 g_hash_table_insert(klog_table, str, entry); 221 } else { 222 free(str); 223 } 224 225 lt_update_stat_value(entry, type, value); 226 } 227