klog.c revision 10111:30fced71ad43
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
39static GHashTable *proc_table = NULL;    /* pid -> char * */
40static GHashTable *klog_table = NULL;    /* char * -> uint64_t total */
41static char klog_filename[PATH_MAX] = DEFAULT_KLOG_FILE;
42static int klog_level = LT_KLOG_LEVEL_NONE;
43
44static void
45print_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
54static void
55print_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 */
67void
68lt_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 */
86int
87lt_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 */
117int
118lt_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 */
132void
133lt_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 */
169void
170lt_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 */
188void
189lt_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