summaryrefslogtreecommitdiff
path: root/usr/src/cmd/latencytop/common/klog.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/latencytop/common/klog.c')
-rw-r--r--usr/src/cmd/latencytop/common/klog.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/usr/src/cmd/latencytop/common/klog.c b/usr/src/cmd/latencytop/common/klog.c
new file mode 100644
index 0000000000..11d14192dc
--- /dev/null
+++ b/usr/src/cmd/latencytop/common/klog.c
@@ -0,0 +1,231 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <procfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "latencytop.h"
+
+static GHashTable *proc_table = NULL; /* pid -> char * */
+static GHashTable *klog_table = NULL; /* char * -> uint64_t total */
+static char klog_filename[PATH_MAX] = DEFAULT_KLOG_FILE;
+static int klog_level = LT_KLOG_LEVEL_NONE;
+
+static void
+print_proc(void *key, const char *args, FILE *fp)
+{
+ pid_t pid = LT_POINTER_TO_INT(key);
+ char tmp[16];
+
+ (void) snprintf(tmp, sizeof (tmp), "%ld,", (long)pid);
+ (void) fprintf(fp, "%-8s \"%s\"\n", tmp, args);
+}
+
+static void
+print_stat(const char *key, lt_stat_data_t *log, FILE *fp)
+{
+ (void) fprintf(fp, "%lld, %lld, %lld, %s\n",
+ (long long)log->total,
+ (long long)log->count,
+ (long long)log->max,
+ key);
+}
+
+/*
+ * Initialize kernel stack logging.
+ */
+void
+lt_klog_init(void)
+{
+ if (klog_table != NULL || proc_table != NULL) {
+ return;
+ }
+
+ klog_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ (GDestroyNotify)free, (GDestroyNotify)free);
+ lt_check_null(klog_table);
+
+ proc_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify)free);
+ lt_check_null(proc_table);
+}
+
+/*
+ * Set log file path.
+ */
+int
+lt_klog_set_log_file(const char *filename)
+{
+ FILE *fp;
+ int file_exist;
+
+ if (strlen(filename) >= sizeof (klog_filename)) {
+ return (-1);
+ }
+
+ file_exist = lt_file_exist(filename);
+ /* Test if we can write to the file */
+ fp = fopen(filename, "a");
+ if (fp == NULL) {
+ return (-2);
+ }
+ (void) fclose(fp);
+ /* Don't leave empty file behind */
+ if (!file_exist) {
+ (void) unlink(filename);
+ }
+
+ (void) strncpy(klog_filename, filename,
+ sizeof (klog_filename));
+
+ return (0);
+}
+
+/*
+ * Set log level.
+ */
+int
+lt_klog_set_log_level(int level)
+{
+ if (level < 0 || level > (int)LT_KLOG_LEVEL_ALL) {
+ return (-1);
+ }
+
+ klog_level = level;
+
+ return (0);
+}
+
+/*
+ * Write the log to file.
+ */
+void
+lt_klog_write(void)
+{
+ FILE *fp;
+ char buffer[32];
+
+ if (klog_level == LT_KLOG_LEVEL_NONE) {
+ return;
+ }
+
+ g_assert(klog_table != NULL && proc_table != NULL);
+
+ fp = fopen(klog_filename, "a");
+ if (fp == NULL) {
+ return;
+ }
+
+ lt_time_str(buffer, sizeof (buffer));
+
+ (void) fprintf(fp, "# Log generated %s by %s\n", buffer, TITLE);
+ (void) fprintf(fp, "# List of processes\n");
+ (void) fprintf(fp, "PID, CMD\n");
+ g_hash_table_foreach(proc_table,
+ (GHFunc)print_proc, fp);
+
+ (void) fprintf(fp, "# Statistics\n");
+ (void) fprintf(fp, "TOTAL, COUNT, MAX, PID, KSTACK\n");
+ g_hash_table_foreach(klog_table,
+ (GHFunc)print_stat, fp);
+
+ (void) fclose(fp);
+}
+
+/*
+ * Clean up function. This will cause all log in memory be written to the
+ * log file.
+ */
+void
+lt_klog_deinit(void)
+{
+ if (klog_table != NULL) {
+ g_hash_table_destroy(klog_table);
+ klog_table = NULL;
+ }
+
+ if (proc_table != NULL) {
+ g_hash_table_destroy(proc_table);
+ proc_table = NULL;
+ }
+}
+
+/*
+ * Log a stack and its statistics. Only "total" will be logged, others are
+ * internally discarded.
+ */
+/* ARGSUSED */
+void
+lt_klog_log(int level, pid_t pid, char *stack,
+ lt_stat_type_t type, uint64_t value)
+{
+ lt_stat_data_t *entry = NULL;
+ char *psargs;
+ char *str;
+ int str_len;
+
+ if ((level & klog_level) == 0) {
+ return;
+ }
+ g_assert(klog_table != NULL && proc_table != NULL);
+
+ psargs = (char *)g_hash_table_lookup(proc_table,
+ LT_INT_TO_POINTER(pid));
+ if (psargs == NULL) {
+ psargs = lt_get_proc_field(pid, LT_FIELD_PSARGS);
+ if (psargs == NULL) {
+ psargs = lt_get_proc_field(pid, LT_FIELD_FNAME);
+ }
+
+ if (psargs == NULL) {
+ return;
+ }
+
+ g_hash_table_insert(proc_table,
+ LT_INT_TO_POINTER(pid), psargs);
+ }
+
+ str_len = strlen(stack) + 20;
+ str = lt_malloc(str_len);
+ (void) snprintf(str, str_len, "%ld, \"%s\"", pid, stack);
+
+ entry = (lt_stat_data_t *)g_hash_table_lookup(klog_table, str);
+ if (entry == NULL) {
+ entry = (lt_stat_data_t *)lt_zalloc(sizeof (lt_stat_data_t));
+ g_hash_table_insert(klog_table, str, entry);
+ } else {
+ free(str);
+ }
+
+ lt_update_stat_value(entry, type, value);
+}