--- old/usr/src/cmd/latencytop/common/table.c	Thu Apr 11 14:30:36 2024
+++ /dev/null	Thu Apr 11 08:00:00 2024
@@ -1,840 +0,0 @@
-/*
- * 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 <stdlib.h>
-#include <string.h>
-#include <memory.h>
-#include <stdio.h>
-#include <ctype.h>
-
-#include "latencytop.h"
-
-/*
- * Structure that holds detail of a cause.
- */
-typedef struct {
-	int lt_c_cause_id;
-	int lt_c_flags;
-	char *lt_c_name;
-} lt_cause_t;
-
-/*
- * Structure that represents a matched cause.
- */
-typedef struct  {
-	int lt_mt_priority;
-	int lt_mt_cause_id;
-} lt_match_t;
-
-/* All lt_cause_t that are created. */
-static GHashTable *cause_lookup = NULL;
-static GPtrArray *causes_array = NULL;
-static int causes_array_len = 0;
-
-/*
- * This hash table maps a symbol to a cause.
- * key is of type "char *" and value is of type "lt_match_t *".
- */
-static GHashTable *symbol_lookup_table = NULL;
-
-/*
- * The dtrace translation rules we get from the script
- */
-char *dtrans = NULL;
-
-/*
- * These structures are only used inside .trans parser.
- */
-typedef struct {
-	int lt_dm_priority;
-	char *lt_dm_macro;
-} lt_dmacro_t;
-
-typedef struct {
-	GSequence *lt_pr_cmd_disable;
-	GHashTable *lt_pr_dmacro;
-} lt_parser_t;
-
-/* ARGSUSED */
-static void
-free_cause(lt_cause_t *cause, void *user)
-{
-	g_assert(cause != NULL && cause->lt_c_name != NULL);
-
-	free(cause->lt_c_name);
-	free(cause);
-}
-
-static void
-free_dmacro(lt_dmacro_t *d)
-{
-	g_assert(d->lt_dm_macro != NULL);
-	free(d->lt_dm_macro);
-	free(d);
-}
-
-/*
- * Add a cause.
- */
-static lt_cause_t *
-new_cause(char *name, int flags)
-{
-	lt_cause_t *entry;
-
-	g_assert(name != NULL);
-
-	entry = (lt_cause_t *)lt_malloc(sizeof (lt_cause_t));
-	entry->lt_c_flags = flags;
-	entry->lt_c_name = name;
-	entry->lt_c_cause_id = causes_array_len;
-
-	g_ptr_array_add(causes_array, entry);
-	++causes_array_len;
-
-	return (entry);
-}
-
-/*
- * Set a cause to "disabled" state.
- */
-static void
-disable_cause(char *cause_str, GHashTable *cause_table)
-{
-	lt_cause_t *cause;
-
-	cause = (lt_cause_t *)g_hash_table_lookup(cause_table, cause_str);
-
-	if (cause != NULL) {
-		cause->lt_c_flags |= CAUSE_FLAG_DISABLED;
-	}
-}
-
-/*
- * Helper functions that reads a line from a character array.
- */
-static int
-read_line_from_mem(const char *mem, int mem_len, char *line, int line_len,
-    int *index)
-{
-	g_assert(mem != NULL && line != NULL && index != NULL);
-
-	if (line_len <= 0 || mem_len <= 0) {
-		return (0);
-	}
-
-	if (*index >= mem_len) {
-		return (0);
-	}
-
-	while (line_len > 1 && *index < mem_len) {
-		*line = mem[(*index)++];
-		--line_len;
-		++line;
-
-		if (*(line-1) == '\r' || *(line-1) == '\n') {
-			break;
-		}
-	}
-	*line = '\0';
-
-	return (1);
-}
-
-/*
- * Parse special command from configuration file. Special command
- * has the following format :
-
- *	disable_cause <cause name>
- */
-static int
-parse_config_cmd(char *begin, lt_parser_t *parser)
-{
-	char *tmp;
-	char old_chr = 0;
-
-	/*
-	 * disable_cause  FSFlush Daemon
-	 * ^
-	 */
-	if (*begin == '\0') {
-		return (0);
-	}
-
-	for (tmp = begin;
-	    *tmp != '\0' && !isspace(*tmp);
-	    ++tmp) {
-	}
-	old_chr = *tmp;
-	*tmp = '\0';
-
-	if (strcmp("disable_cause", begin) == 0) {
-		if (old_chr == '\0') {
-			/* Must have an argument */
-			lt_display_error(
-			    "Invalid command format: %s\n",
-			    begin);
-			return (-1);
-		}
-
-		begin = tmp+1;
-		while (isspace(*begin)) {
-			++begin;
-		}
-
-		g_sequence_append(parser->lt_pr_cmd_disable,
-		    lt_strdup(begin));
-	} else   {
-		*tmp = old_chr;
-		lt_display_error(
-		    "Unknown command: %s\n", begin);
-		return (-1);
-	}
-
-	return (0);
-}
-
-/*
- * Parse symbol translation from configuration file. Symbol translation
- * has the following format :
- *
- *	<priority> <symbol name> <cause>
- *
- * Finally check if that cause has already been mapped.
- */
-static int
-parse_sym_trans(char *begin)
-{
-	int priority = 0;
-	char *match;
-	char *match_dup;
-	char *cause_str;
-	lt_cause_t *cause;
-	lt_match_t *match_entry;
-	char *tmp;
-
-	/*
-	 * 10	genunix`pread			Syscall pread
-	 * ^
-	 */
-	priority = strtol(begin, &tmp, 10);
-
-	if (tmp == begin || priority == 0) {
-		return (-1);
-	}
-
-	begin = tmp;
-
-	/*
-	 * 10	genunix`pread			Syscall pread
-	 * --^
-	 */
-
-	if (!isspace(*begin)) {
-		/* At least one space char after <priority> */
-		return (-1);
-	}
-
-	while (isspace(*begin)) {
-		++begin;
-	}
-
-	if (*begin == 0) {
-		return (-1);
-	}
-
-	/*
-	 * 10	genunix`pread			Syscall pread
-	 * -----^
-	 */
-	for (tmp = begin;
-	    *tmp != '\0' && !isspace(*tmp);
-	    ++tmp) {
-	}
-
-	if (*tmp == '\0') {
-		return (-1);
-	}
-
-	*tmp = '\0';
-	match = begin;
-
-	/* Check if we have mapped this function before. */
-	match_entry = (lt_match_t *)
-	    g_hash_table_lookup(symbol_lookup_table, match);
-
-	if (match_entry != NULL &&
-	    HIGHER_PRIORITY(match_entry->lt_mt_priority, priority)) {
-		/* We already have a higher entry. Ignore this. */
-		return (0);
-	}
-
-	begin = tmp + 1;
-
-	/*
-	 * 10	genunix`pread			Syscall pread
-	 * -------------------------------------^
-	 */
-	while (isspace(*begin)) {
-		++begin;
-	}
-
-	if (*begin == 0) {
-		return (-1);
-	}
-
-	cause_str = begin;
-
-	/* Check if we have mapped this cause before. */
-	cause = (lt_cause_t *)
-	    g_hash_table_lookup(cause_lookup, cause_str);
-
-	if (cause == NULL) {
-		char *cause_dup = lt_strdup(cause_str);
-		cause = new_cause(cause_dup, 0);
-		g_hash_table_insert(cause_lookup, cause_dup, cause);
-	}
-
-	match_entry = (lt_match_t *)lt_malloc(sizeof (lt_match_t));
-	match_entry->lt_mt_priority = priority;
-	match_entry->lt_mt_cause_id = cause->lt_c_cause_id;
-	match_dup = lt_strdup(match);
-
-	g_hash_table_insert(symbol_lookup_table, match_dup,
-	    match_entry);
-
-	return (0);
-}
-
-/*
- * Parse D macro. D macros have the following format :
- *
- *	<priority> <entry probe> <return probe> <cause>
- *
- * Finally check if that cause has already been mapped.
- */
-static int
-parse_dmacro(char *begin, lt_parser_t *parser)
-{
-	int priority = 0;
-	char *entryprobe;
-	char *returnprobe;
-	char *cause_str;
-	char buf[512];
-	char probepair[512];
-	char *tmp = NULL;
-	lt_cause_t *cause;
-	lt_dmacro_t *dmacro;
-
-	/*
-	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
-	 * ^
-	 */
-	priority = strtol(begin, &tmp, 10);
-
-	if (tmp == begin || priority == 0) {
-		return (-1);
-	}
-
-	begin = tmp;
-
-	/*
-	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
-	 * --^
-	 */
-	while (isspace(*begin)) {
-		++begin;
-	}
-
-	if (*begin == 0) {
-		return (-1);
-	}
-
-	/*
-	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
-	 * -----^
-	 */
-	for (tmp = begin;
-	    *tmp != '\0' && !isspace(*tmp);
-	    ++tmp) {
-	}
-
-	if (*tmp == '\0') {
-		return (-1);
-	}
-
-	*tmp = '\0';
-	entryprobe = begin;
-	begin = tmp + 1;
-
-	while (isspace(*begin)) {
-		++begin;
-	}
-
-	/*
-	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
-	 * -----------------------------^
-	 */
-	for (tmp = begin;
-	    *tmp != '\0' && !isspace(*tmp);
-	    ++tmp) {
-	}
-
-	if (*tmp == '\0') {
-		return (-1);
-	}
-
-	*tmp = '\0';
-	returnprobe = begin;
-	begin = tmp + 1;
-
-	while (isspace(*begin)) {
-		++begin;
-	}
-
-	/*
-	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
-	 * -----------------------------------------------------^
-	 */
-	if (*begin == 0) {
-		return (-1);
-	}
-
-	cause_str = begin;
-
-	dmacro = NULL;
-
-	/* Check if we have mapped this cause before. */
-	cause = (lt_cause_t *)
-	    g_hash_table_lookup(cause_lookup, cause_str);
-
-	if (cause == NULL) {
-		char *cause_dup = lt_strdup(cause_str);
-		cause = new_cause(cause_dup, 0);
-		g_hash_table_insert(cause_lookup, cause_dup, cause);
-	}
-
-	(void) snprintf(buf, sizeof (buf), "\nTRANSLATE(%s, %s, \"%s\", %d)\n",
-	    entryprobe, returnprobe, cause_str, priority);
-
-	(void) snprintf(probepair, sizeof (probepair), "%s %s", entryprobe,
-	    returnprobe);
-
-	g_assert(cause != NULL);
-	g_assert(parser->lt_pr_dmacro != NULL);
-
-	dmacro = g_hash_table_lookup(parser->lt_pr_dmacro, probepair);
-
-	if (dmacro == NULL) {
-		dmacro = (lt_dmacro_t *)lt_malloc(sizeof (lt_dmacro_t));
-		dmacro->lt_dm_priority = priority;
-		dmacro->lt_dm_macro = lt_strdup(buf);
-		g_hash_table_insert(parser->lt_pr_dmacro, lt_strdup(probepair),
-		    dmacro);
-	} else if (dmacro->lt_dm_priority < priority) {
-		free(dmacro->lt_dm_macro);
-		dmacro->lt_dm_priority = priority;
-		dmacro->lt_dm_macro = lt_strdup(buf);
-	}
-
-	return (0);
-}
-
-/*
- * Helper function to collect TRANSLATE() macros.
- */
-/* ARGSUSED */
-static void
-genscript(void *key, lt_dmacro_t *dmacro, GString *str)
-{
-	g_string_append(str, dmacro->lt_dm_macro);
-}
-
-/*
- * Main logic that parses translation rules one line at a time,
- * and creates a lookup table from it. The syntax for the translation
- * is as follows :
- *
- *	#				<--- comment
- *	D <D macro rule>		<--- D macro
- *	S <Symbol translation>		<--- Symbols
- *	disable_cause <cause>		<--- special command
- */
-static int
-parse_config(const char *work, int work_len)
-{
-	char line[256];
-	int len;
-	char *begin, *end;
-	int current = 0;
-	lt_parser_t parser;
-	int ret = 0;
-	char flag;
-	GString *script;
-
-	cause_lookup = g_hash_table_new(g_str_hash, g_str_equal);
-	lt_check_null(cause_lookup);
-
-	parser.lt_pr_cmd_disable = g_sequence_new((GDestroyNotify)free);
-	lt_check_null(parser.lt_pr_cmd_disable);
-
-	parser.lt_pr_dmacro = g_hash_table_new_full(g_str_hash,
-	    g_str_equal, (GDestroyNotify)free, (GDestroyNotify)free_dmacro);
-	lt_check_null(parser.lt_pr_dmacro);
-
-	while (read_line_from_mem(work, work_len, line, sizeof (line),
-	    &current)) {
-		len = strlen(line);
-
-		if (line[len-1] != '\n' && line[len-1] != '\r' &&
-		    current < work_len) {
-			lt_display_error("Configuration line too long.\n");
-			goto err;
-		}
-
-		begin = line;
-
-		while (isspace(*begin)) {
-			++begin;
-		}
-
-		if (*begin == '\0') {
-			/* Ignore empty line */
-			continue;
-		}
-
-		/* Delete trailing spaces. */
-		end = begin + strlen(begin) - 1;
-
-		while (isspace(*end)) {
-			--end;
-		}
-
-		end[1] = '\0';
-
-		flag = *begin;
-		++begin;
-
-		switch (flag) {
-		case '#':
-			ret = 0;
-			break;
-		case ';':
-			ret = parse_config_cmd(begin, &parser);
-			break;
-		case 'D':
-		case 'd':
-			if (!isspace(*begin)) {
-				lt_display_error(
-				    "No space after flag char: %s\n", line);
-			}
-			while (isspace(*begin)) {
-				++begin;
-			}
-			ret = parse_dmacro(begin, &parser);
-			break;
-		case 'S':
-		case 's':
-			if (!isspace(*begin)) {
-				lt_display_error(
-				    "No space after flag char: %s\n", line);
-			}
-			while (isspace(*begin)) {
-				++begin;
-			}
-			ret = parse_sym_trans(begin);
-			break;
-		default:
-			ret = -1;
-			break;
-		}
-
-		if (ret != 0) {
-			lt_display_error(
-			    "Invalid configuration line: %s\n", line);
-			goto err;
-		}
-	}
-
-	script = g_string_new(NULL);
-	g_hash_table_foreach(parser.lt_pr_dmacro, (GHFunc)genscript, script);
-	dtrans = g_string_free(script, FALSE);
-
-	if (dtrans != NULL && strlen(dtrans) == 0) {
-		free(dtrans);
-		dtrans = NULL;
-	}
-
-	g_sequence_foreach(parser.lt_pr_cmd_disable, (GFunc)disable_cause,
-	    cause_lookup);
-	g_sequence_free(parser.lt_pr_cmd_disable);
-
-	return (0);
-
-err:
-	g_sequence_free(parser.lt_pr_cmd_disable);
-	g_hash_table_destroy(parser.lt_pr_dmacro);
-	return (-1);
-
-}
-
-/*
- * Init function, called when latencytop starts.
- * It loads translation rules from the configuration file. The configuration
- * file defines some causes and symbols that match those causes.
- */
-int
-lt_table_init(void)
-{
-	char *config_loaded = NULL;
-	int config_loaded_len = 0;
-	const char *work = NULL;
-	int work_len = 0;
-	lt_cause_t *cause;
-
-#ifdef EMBED_CONFIGS
-	work = &latencytop_trans_start;
-	work_len = (int)(&latencytop_trans_end - &latencytop_trans_start);
-#endif
-
-	if (g_config.lt_cfg_config_name != NULL) {
-		FILE *fp;
-		fp = fopen(g_config.lt_cfg_config_name, "r");
-
-		if (NULL == fp) {
-			lt_display_error(
-			    "Unable to open configuration file.\n");
-			return (-1);
-		}
-
-		(void) fseek(fp, 0, SEEK_END);
-		config_loaded_len = (int)ftell(fp);
-		config_loaded = (char *)lt_malloc(config_loaded_len);
-		(void) fseek(fp, 0, SEEK_SET);
-
-		/* A zero-byte translation is valid */
-		if (config_loaded_len != 0 &&
-		    fread(config_loaded, config_loaded_len, 1, fp) == 0) {
-			lt_display_error(
-			    "Unable to read configuration file.\n");
-			(void) fclose(fp);
-			free(config_loaded);
-			return (-1);
-		}
-
-		(void) fclose(fp);
-		(void) printf("Loaded configuration from %s\n",
-		    g_config.lt_cfg_config_name);
-
-		work = config_loaded;
-		work_len = config_loaded_len;
-	}
-
-	lt_table_deinit();
-	causes_array = g_ptr_array_new();
-	lt_check_null(causes_array);
-
-	/* 0 is not used, but it is kept as a place for bugs etc. */
-	cause = new_cause(lt_strdup("Nothing"), CAUSE_FLAG_DISABLED);
-	g_assert(cause->lt_c_cause_id == INVALID_CAUSE);
-
-	symbol_lookup_table = g_hash_table_new_full(
-	    g_str_hash, g_str_equal,
-	    (GDestroyNotify)free, (GDestroyNotify)free);
-	lt_check_null(symbol_lookup_table);
-
-	if (work_len != 0 && parse_config(work, work_len) != 0) {
-		return (-1);
-	}
-
-	if (config_loaded != NULL) {
-		free(config_loaded);
-	}
-
-	return (0);
-}
-
-/*
- * Some causes, such as "lock spinning", do not have stack trace. Names
- * of such causes are explicitly specified in the D script.
- * This function resolves such causes and dynamically adds them
- * to the global tables when they are found first. If auto_create is set
- * to TRUE, the entry will be created if it is not found.
- * Return cause_id of the cause.
- */
-int
-lt_table_cause_from_name(char *name, int auto_create, int flags)
-{
-	lt_cause_t *cause = NULL;
-
-	if (cause_lookup == NULL) {
-		cause_lookup = g_hash_table_new(g_str_hash, g_str_equal);
-		lt_check_null(cause_lookup);
-	} else   {
-		cause = (lt_cause_t *)
-		    g_hash_table_lookup(cause_lookup, name);
-	}
-
-	if (cause == NULL && auto_create) {
-		char *cause_dup;
-
-		if (name[0] == '#') {
-			flags |= CAUSE_FLAG_HIDE_IN_SUMMARY;
-		}
-
-		cause_dup = lt_strdup(name);
-		cause = new_cause(cause_dup, flags);
-		g_hash_table_insert(cause_lookup, cause_dup, cause);
-	}
-
-	return (cause == NULL ? INVALID_CAUSE : cause->lt_c_cause_id);
-}
-
-/*
- * Try to map a symbol on stack to a known cause.
- * module_func has the format "module_name`function_name".
- * cause_id and priority will be set if a cause is found.
- * If cause is found return 1, otherwise return 0.
- */
-int
-lt_table_cause_from_stack(const char *module_func, int *cause_id, int *priority)
-{
-	lt_match_t *match;
-
-	g_assert(module_func != NULL && cause_id != NULL && priority != NULL);
-
-	if (symbol_lookup_table == NULL) {
-		return (0);
-	}
-
-	match = (lt_match_t *)
-	    g_hash_table_lookup(symbol_lookup_table, module_func);
-
-	if (match == NULL) {
-		char *func = strchr(module_func, '`');
-
-		if (func != NULL) {
-			match = (lt_match_t *)
-			    g_hash_table_lookup(symbol_lookup_table, func);
-		}
-	}
-
-	if (match == NULL) {
-		return (0);
-	} else   {
-		*cause_id = match->lt_mt_cause_id;
-		*priority = match->lt_mt_priority;
-		return (1);
-	}
-}
-
-/*
- * Get the display name of a cause. cause_id must be valid,
- * it is usually returned from lt_table_cause_from_stack() or
- * lt_table_cause_from_name().
- */
-const char *
-lt_table_get_cause_name(int cause_id)
-{
-	lt_cause_t *cause;
-
-	if (cause_id < 0 || cause_id >= causes_array_len) {
-		return (NULL);
-	}
-
-	cause = (lt_cause_t *)g_ptr_array_index(causes_array, cause_id);
-
-	if (cause == NULL) {
-		return (NULL);
-	} else {
-		return (cause->lt_c_name);
-	}
-}
-
-/*
- * Check cause flag.
- * If CAUSE_ALL_FLAGS is passed in, all flags are returned.
- */
-int
-lt_table_get_cause_flag(int cause_id, int flag)
-{
-	lt_cause_t *cause;
-
-	if (cause_id < 0 || cause_id >= causes_array_len) {
-		return (0);
-	}
-
-	cause = (lt_cause_t *)g_ptr_array_index(causes_array, cause_id);
-
-	if (cause == NULL) {
-		return (0);
-	} else {
-		return (cause->lt_c_flags & flag);
-	}
-}
-
-/*
- * Append macros to D script, if any.
- */
-int
-lt_table_append_trans(FILE *fp)
-{
-	if (dtrans != NULL) {
-		if (fwrite(dtrans, strlen(dtrans), 1, fp) != 1) {
-			return (-1);
-		}
-	}
-
-	return (0);
-}
-
-/*
- * Clean up function.
- * Free the resources used for symbol table (symbols, causes etc.).
- */
-void
-lt_table_deinit(void)
-{
-	if (symbol_lookup_table != NULL) {
-		g_hash_table_destroy(symbol_lookup_table);
-		symbol_lookup_table = NULL;
-	}
-
-	if (cause_lookup != NULL) {
-		g_hash_table_destroy(cause_lookup);
-		cause_lookup = NULL;
-	}
-
-	if (causes_array != NULL) {
-		g_ptr_array_foreach(causes_array, (GFunc)free_cause, NULL);
-		g_ptr_array_free(causes_array, TRUE);
-		causes_array = NULL;
-		causes_array_len = 0;
-	}
-
-	if (dtrans != NULL) {
-		g_free(dtrans);
-		dtrans = NULL;
-	}
-}
--- /dev/null	Thu Apr 11 08:00:00 2024
+++ new/usr/src/cmd/latencytop/table.c	Thu Apr 11 14:30:36 2024
@@ -0,0 +1,840 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "latencytop.h"
+
+/*
+ * Structure that holds detail of a cause.
+ */
+typedef struct {
+	int lt_c_cause_id;
+	int lt_c_flags;
+	char *lt_c_name;
+} lt_cause_t;
+
+/*
+ * Structure that represents a matched cause.
+ */
+typedef struct  {
+	int lt_mt_priority;
+	int lt_mt_cause_id;
+} lt_match_t;
+
+/* All lt_cause_t that are created. */
+static GHashTable *cause_lookup = NULL;
+static GPtrArray *causes_array = NULL;
+static int causes_array_len = 0;
+
+/*
+ * This hash table maps a symbol to a cause.
+ * key is of type "char *" and value is of type "lt_match_t *".
+ */
+static GHashTable *symbol_lookup_table = NULL;
+
+/*
+ * The dtrace translation rules we get from the script
+ */
+char *dtrans = NULL;
+
+/*
+ * These structures are only used inside .trans parser.
+ */
+typedef struct {
+	int lt_dm_priority;
+	char *lt_dm_macro;
+} lt_dmacro_t;
+
+typedef struct {
+	GSequence *lt_pr_cmd_disable;
+	GHashTable *lt_pr_dmacro;
+} lt_parser_t;
+
+/* ARGSUSED */
+static void
+free_cause(lt_cause_t *cause, void *user)
+{
+	g_assert(cause != NULL && cause->lt_c_name != NULL);
+
+	free(cause->lt_c_name);
+	free(cause);
+}
+
+static void
+free_dmacro(lt_dmacro_t *d)
+{
+	g_assert(d->lt_dm_macro != NULL);
+	free(d->lt_dm_macro);
+	free(d);
+}
+
+/*
+ * Add a cause.
+ */
+static lt_cause_t *
+new_cause(char *name, int flags)
+{
+	lt_cause_t *entry;
+
+	g_assert(name != NULL);
+
+	entry = (lt_cause_t *)lt_malloc(sizeof (lt_cause_t));
+	entry->lt_c_flags = flags;
+	entry->lt_c_name = name;
+	entry->lt_c_cause_id = causes_array_len;
+
+	g_ptr_array_add(causes_array, entry);
+	++causes_array_len;
+
+	return (entry);
+}
+
+/*
+ * Set a cause to "disabled" state.
+ */
+static void
+disable_cause(char *cause_str, GHashTable *cause_table)
+{
+	lt_cause_t *cause;
+
+	cause = (lt_cause_t *)g_hash_table_lookup(cause_table, cause_str);
+
+	if (cause != NULL) {
+		cause->lt_c_flags |= CAUSE_FLAG_DISABLED;
+	}
+}
+
+/*
+ * Helper functions that reads a line from a character array.
+ */
+static int
+read_line_from_mem(const char *mem, int mem_len, char *line, int line_len,
+    int *index)
+{
+	g_assert(mem != NULL && line != NULL && index != NULL);
+
+	if (line_len <= 0 || mem_len <= 0) {
+		return (0);
+	}
+
+	if (*index >= mem_len) {
+		return (0);
+	}
+
+	while (line_len > 1 && *index < mem_len) {
+		*line = mem[(*index)++];
+		--line_len;
+		++line;
+
+		if (*(line-1) == '\r' || *(line-1) == '\n') {
+			break;
+		}
+	}
+	*line = '\0';
+
+	return (1);
+}
+
+/*
+ * Parse special command from configuration file. Special command
+ * has the following format :
+ *
+ *	disable_cause <cause name>
+ */
+static int
+parse_config_cmd(char *begin, lt_parser_t *parser)
+{
+	char *tmp;
+	char old_chr = 0;
+
+	/*
+	 * disable_cause  FSFlush Daemon
+	 * ^
+	 */
+	if (*begin == '\0') {
+		return (0);
+	}
+
+	for (tmp = begin;
+	    *tmp != '\0' && !isspace(*tmp);
+	    ++tmp) {
+	}
+	old_chr = *tmp;
+	*tmp = '\0';
+
+	if (strcmp("disable_cause", begin) == 0) {
+		if (old_chr == '\0') {
+			/* Must have an argument */
+			lt_display_error(
+			    "Invalid command format: %s\n",
+			    begin);
+			return (-1);
+		}
+
+		begin = tmp+1;
+		while (isspace(*begin)) {
+			++begin;
+		}
+
+		g_sequence_append(parser->lt_pr_cmd_disable,
+		    lt_strdup(begin));
+	} else   {
+		*tmp = old_chr;
+		lt_display_error(
+		    "Unknown command: %s\n", begin);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Parse symbol translation from configuration file. Symbol translation
+ * has the following format :
+ *
+ *	<priority> <symbol name> <cause>
+ *
+ * Finally check if that cause has already been mapped.
+ */
+static int
+parse_sym_trans(char *begin)
+{
+	int priority = 0;
+	char *match;
+	char *match_dup;
+	char *cause_str;
+	lt_cause_t *cause;
+	lt_match_t *match_entry;
+	char *tmp;
+
+	/*
+	 * 10	genunix`pread			Syscall pread
+	 * ^
+	 */
+	priority = strtol(begin, &tmp, 10);
+
+	if (tmp == begin || priority == 0) {
+		return (-1);
+	}
+
+	begin = tmp;
+
+	/*
+	 * 10	genunix`pread			Syscall pread
+	 * --^
+	 */
+
+	if (!isspace(*begin)) {
+		/* At least one space char after <priority> */
+		return (-1);
+	}
+
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	if (*begin == 0) {
+		return (-1);
+	}
+
+	/*
+	 * 10	genunix`pread			Syscall pread
+	 * -----^
+	 */
+	for (tmp = begin;
+	    *tmp != '\0' && !isspace(*tmp);
+	    ++tmp) {
+	}
+
+	if (*tmp == '\0') {
+		return (-1);
+	}
+
+	*tmp = '\0';
+	match = begin;
+
+	/* Check if we have mapped this function before. */
+	match_entry = (lt_match_t *)
+	    g_hash_table_lookup(symbol_lookup_table, match);
+
+	if (match_entry != NULL &&
+	    HIGHER_PRIORITY(match_entry->lt_mt_priority, priority)) {
+		/* We already have a higher entry. Ignore this. */
+		return (0);
+	}
+
+	begin = tmp + 1;
+
+	/*
+	 * 10	genunix`pread			Syscall pread
+	 * -------------------------------------^
+	 */
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	if (*begin == 0) {
+		return (-1);
+	}
+
+	cause_str = begin;
+
+	/* Check if we have mapped this cause before. */
+	cause = (lt_cause_t *)
+	    g_hash_table_lookup(cause_lookup, cause_str);
+
+	if (cause == NULL) {
+		char *cause_dup = lt_strdup(cause_str);
+		cause = new_cause(cause_dup, 0);
+		g_hash_table_insert(cause_lookup, cause_dup, cause);
+	}
+
+	match_entry = (lt_match_t *)lt_malloc(sizeof (lt_match_t));
+	match_entry->lt_mt_priority = priority;
+	match_entry->lt_mt_cause_id = cause->lt_c_cause_id;
+	match_dup = lt_strdup(match);
+
+	g_hash_table_insert(symbol_lookup_table, match_dup,
+	    match_entry);
+
+	return (0);
+}
+
+/*
+ * Parse D macro. D macros have the following format :
+ *
+ *	<priority> <entry probe> <return probe> <cause>
+ *
+ * Finally check if that cause has already been mapped.
+ */
+static int
+parse_dmacro(char *begin, lt_parser_t *parser)
+{
+	int priority = 0;
+	char *entryprobe;
+	char *returnprobe;
+	char *cause_str;
+	char buf[512];
+	char probepair[512];
+	char *tmp = NULL;
+	lt_cause_t *cause;
+	lt_dmacro_t *dmacro;
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * ^
+	 */
+	priority = strtol(begin, &tmp, 10);
+
+	if (tmp == begin || priority == 0) {
+		return (-1);
+	}
+
+	begin = tmp;
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * --^
+	 */
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	if (*begin == 0) {
+		return (-1);
+	}
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * -----^
+	 */
+	for (tmp = begin;
+	    *tmp != '\0' && !isspace(*tmp);
+	    ++tmp) {
+	}
+
+	if (*tmp == '\0') {
+		return (-1);
+	}
+
+	*tmp = '\0';
+	entryprobe = begin;
+	begin = tmp + 1;
+
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * -----------------------------^
+	 */
+	for (tmp = begin;
+	    *tmp != '\0' && !isspace(*tmp);
+	    ++tmp) {
+	}
+
+	if (*tmp == '\0') {
+		return (-1);
+	}
+
+	*tmp = '\0';
+	returnprobe = begin;
+	begin = tmp + 1;
+
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * -----------------------------------------------------^
+	 */
+	if (*begin == 0) {
+		return (-1);
+	}
+
+	cause_str = begin;
+
+	dmacro = NULL;
+
+	/* Check if we have mapped this cause before. */
+	cause = (lt_cause_t *)
+	    g_hash_table_lookup(cause_lookup, cause_str);
+
+	if (cause == NULL) {
+		char *cause_dup = lt_strdup(cause_str);
+		cause = new_cause(cause_dup, 0);
+		g_hash_table_insert(cause_lookup, cause_dup, cause);
+	}
+
+	(void) snprintf(buf, sizeof (buf), "\nTRANSLATE(%s, %s, \"%s\", %d)\n",
+	    entryprobe, returnprobe, cause_str, priority);
+
+	(void) snprintf(probepair, sizeof (probepair), "%s %s", entryprobe,
+	    returnprobe);
+
+	g_assert(cause != NULL);
+	g_assert(parser->lt_pr_dmacro != NULL);
+
+	dmacro = g_hash_table_lookup(parser->lt_pr_dmacro, probepair);
+
+	if (dmacro == NULL) {
+		dmacro = (lt_dmacro_t *)lt_malloc(sizeof (lt_dmacro_t));
+		dmacro->lt_dm_priority = priority;
+		dmacro->lt_dm_macro = lt_strdup(buf);
+		g_hash_table_insert(parser->lt_pr_dmacro, lt_strdup(probepair),
+		    dmacro);
+	} else if (dmacro->lt_dm_priority < priority) {
+		free(dmacro->lt_dm_macro);
+		dmacro->lt_dm_priority = priority;
+		dmacro->lt_dm_macro = lt_strdup(buf);
+	}
+
+	return (0);
+}
+
+/*
+ * Helper function to collect TRANSLATE() macros.
+ */
+/* ARGSUSED */
+static void
+genscript(void *key, lt_dmacro_t *dmacro, GString *str)
+{
+	g_string_append(str, dmacro->lt_dm_macro);
+}
+
+/*
+ * Main logic that parses translation rules one line at a time,
+ * and creates a lookup table from it. The syntax for the translation
+ * is as follows :
+ *
+ *	#				<--- comment
+ *	D <D macro rule>		<--- D macro
+ *	S <Symbol translation>		<--- Symbols
+ *	disable_cause <cause>		<--- special command
+ */
+static int
+parse_config(const char *work, int work_len)
+{
+	char line[256];
+	int len;
+	char *begin, *end;
+	int current = 0;
+	lt_parser_t parser;
+	int ret = 0;
+	char flag;
+	GString *script;
+
+	cause_lookup = g_hash_table_new(g_str_hash, g_str_equal);
+	lt_check_null(cause_lookup);
+
+	parser.lt_pr_cmd_disable = g_sequence_new((GDestroyNotify)free);
+	lt_check_null(parser.lt_pr_cmd_disable);
+
+	parser.lt_pr_dmacro = g_hash_table_new_full(g_str_hash,
+	    g_str_equal, (GDestroyNotify)free, (GDestroyNotify)free_dmacro);
+	lt_check_null(parser.lt_pr_dmacro);
+
+	while (read_line_from_mem(work, work_len, line, sizeof (line),
+	    &current)) {
+		len = strlen(line);
+
+		if (line[len-1] != '\n' && line[len-1] != '\r' &&
+		    current < work_len) {
+			lt_display_error("Configuration line too long.\n");
+			goto err;
+		}
+
+		begin = line;
+
+		while (isspace(*begin)) {
+			++begin;
+		}
+
+		if (*begin == '\0') {
+			/* Ignore empty line */
+			continue;
+		}
+
+		/* Delete trailing spaces. */
+		end = begin + strlen(begin) - 1;
+
+		while (isspace(*end)) {
+			--end;
+		}
+
+		end[1] = '\0';
+
+		flag = *begin;
+		++begin;
+
+		switch (flag) {
+		case '#':
+			ret = 0;
+			break;
+		case ';':
+			ret = parse_config_cmd(begin, &parser);
+			break;
+		case 'D':
+		case 'd':
+			if (!isspace(*begin)) {
+				lt_display_error(
+				    "No space after flag char: %s\n", line);
+			}
+			while (isspace(*begin)) {
+				++begin;
+			}
+			ret = parse_dmacro(begin, &parser);
+			break;
+		case 'S':
+		case 's':
+			if (!isspace(*begin)) {
+				lt_display_error(
+				    "No space after flag char: %s\n", line);
+			}
+			while (isspace(*begin)) {
+				++begin;
+			}
+			ret = parse_sym_trans(begin);
+			break;
+		default:
+			ret = -1;
+			break;
+		}
+
+		if (ret != 0) {
+			lt_display_error(
+			    "Invalid configuration line: %s\n", line);
+			goto err;
+		}
+	}
+
+	script = g_string_new(NULL);
+	g_hash_table_foreach(parser.lt_pr_dmacro, (GHFunc)genscript, script);
+	dtrans = g_string_free(script, FALSE);
+
+	if (dtrans != NULL && strlen(dtrans) == 0) {
+		free(dtrans);
+		dtrans = NULL;
+	}
+
+	g_sequence_foreach(parser.lt_pr_cmd_disable, (GFunc)disable_cause,
+	    cause_lookup);
+	g_sequence_free(parser.lt_pr_cmd_disable);
+
+	return (0);
+
+err:
+	g_sequence_free(parser.lt_pr_cmd_disable);
+	g_hash_table_destroy(parser.lt_pr_dmacro);
+	return (-1);
+
+}
+
+/*
+ * Init function, called when latencytop starts.
+ * It loads translation rules from the configuration file. The configuration
+ * file defines some causes and symbols that match those causes.
+ */
+int
+lt_table_init(void)
+{
+	char *config_loaded = NULL;
+	int config_loaded_len = 0;
+	const char *work = NULL;
+	int work_len = 0;
+	lt_cause_t *cause;
+
+#ifdef EMBED_CONFIGS
+	work = &latencytop_trans_start;
+	work_len = (int)(&latencytop_trans_end - &latencytop_trans_start);
+#endif
+
+	if (g_config.lt_cfg_config_name != NULL) {
+		FILE *fp;
+		fp = fopen(g_config.lt_cfg_config_name, "r");
+
+		if (NULL == fp) {
+			lt_display_error(
+			    "Unable to open configuration file.\n");
+			return (-1);
+		}
+
+		(void) fseek(fp, 0, SEEK_END);
+		config_loaded_len = (int)ftell(fp);
+		config_loaded = (char *)lt_malloc(config_loaded_len);
+		(void) fseek(fp, 0, SEEK_SET);
+
+		/* A zero-byte translation is valid */
+		if (config_loaded_len != 0 &&
+		    fread(config_loaded, config_loaded_len, 1, fp) == 0) {
+			lt_display_error(
+			    "Unable to read configuration file.\n");
+			(void) fclose(fp);
+			free(config_loaded);
+			return (-1);
+		}
+
+		(void) fclose(fp);
+		(void) printf("Loaded configuration from %s\n",
+		    g_config.lt_cfg_config_name);
+
+		work = config_loaded;
+		work_len = config_loaded_len;
+	}
+
+	lt_table_deinit();
+	causes_array = g_ptr_array_new();
+	lt_check_null(causes_array);
+
+	/* 0 is not used, but it is kept as a place for bugs etc. */
+	cause = new_cause(lt_strdup("Nothing"), CAUSE_FLAG_DISABLED);
+	g_assert(cause->lt_c_cause_id == INVALID_CAUSE);
+
+	symbol_lookup_table = g_hash_table_new_full(
+	    g_str_hash, g_str_equal,
+	    (GDestroyNotify)free, (GDestroyNotify)free);
+	lt_check_null(symbol_lookup_table);
+
+	if (work_len != 0 && parse_config(work, work_len) != 0) {
+		return (-1);
+	}
+
+	if (config_loaded != NULL) {
+		free(config_loaded);
+	}
+
+	return (0);
+}
+
+/*
+ * Some causes, such as "lock spinning", do not have stack trace. Names
+ * of such causes are explicitly specified in the D script.
+ * This function resolves such causes and dynamically adds them
+ * to the global tables when they are found first. If auto_create is set
+ * to TRUE, the entry will be created if it is not found.
+ * Return cause_id of the cause.
+ */
+int
+lt_table_cause_from_name(char *name, int auto_create, int flags)
+{
+	lt_cause_t *cause = NULL;
+
+	if (cause_lookup == NULL) {
+		cause_lookup = g_hash_table_new(g_str_hash, g_str_equal);
+		lt_check_null(cause_lookup);
+	} else   {
+		cause = (lt_cause_t *)
+		    g_hash_table_lookup(cause_lookup, name);
+	}
+
+	if (cause == NULL && auto_create) {
+		char *cause_dup;
+
+		if (name[0] == '#') {
+			flags |= CAUSE_FLAG_HIDE_IN_SUMMARY;
+		}
+
+		cause_dup = lt_strdup(name);
+		cause = new_cause(cause_dup, flags);
+		g_hash_table_insert(cause_lookup, cause_dup, cause);
+	}
+
+	return (cause == NULL ? INVALID_CAUSE : cause->lt_c_cause_id);
+}
+
+/*
+ * Try to map a symbol on stack to a known cause.
+ * module_func has the format "module_name`function_name".
+ * cause_id and priority will be set if a cause is found.
+ * If cause is found return 1, otherwise return 0.
+ */
+int
+lt_table_cause_from_stack(const char *module_func, int *cause_id, int *priority)
+{
+	lt_match_t *match;
+
+	g_assert(module_func != NULL && cause_id != NULL && priority != NULL);
+
+	if (symbol_lookup_table == NULL) {
+		return (0);
+	}
+
+	match = (lt_match_t *)
+	    g_hash_table_lookup(symbol_lookup_table, module_func);
+
+	if (match == NULL) {
+		char *func = strchr(module_func, '`');
+
+		if (func != NULL) {
+			match = (lt_match_t *)
+			    g_hash_table_lookup(symbol_lookup_table, func);
+		}
+	}
+
+	if (match == NULL) {
+		return (0);
+	} else   {
+		*cause_id = match->lt_mt_cause_id;
+		*priority = match->lt_mt_priority;
+		return (1);
+	}
+}
+
+/*
+ * Get the display name of a cause. cause_id must be valid,
+ * it is usually returned from lt_table_cause_from_stack() or
+ * lt_table_cause_from_name().
+ */
+const char *
+lt_table_get_cause_name(int cause_id)
+{
+	lt_cause_t *cause;
+
+	if (cause_id < 0 || cause_id >= causes_array_len) {
+		return (NULL);
+	}
+
+	cause = (lt_cause_t *)g_ptr_array_index(causes_array, cause_id);
+
+	if (cause == NULL) {
+		return (NULL);
+	} else {
+		return (cause->lt_c_name);
+	}
+}
+
+/*
+ * Check cause flag.
+ * If CAUSE_ALL_FLAGS is passed in, all flags are returned.
+ */
+int
+lt_table_get_cause_flag(int cause_id, int flag)
+{
+	lt_cause_t *cause;
+
+	if (cause_id < 0 || cause_id >= causes_array_len) {
+		return (0);
+	}
+
+	cause = (lt_cause_t *)g_ptr_array_index(causes_array, cause_id);
+
+	if (cause == NULL) {
+		return (0);
+	} else {
+		return (cause->lt_c_flags & flag);
+	}
+}
+
+/*
+ * Append macros to D script, if any.
+ */
+int
+lt_table_append_trans(FILE *fp)
+{
+	if (dtrans != NULL) {
+		if (fwrite(dtrans, strlen(dtrans), 1, fp) != 1) {
+			return (-1);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Clean up function.
+ * Free the resources used for symbol table (symbols, causes etc.).
+ */
+void
+lt_table_deinit(void)
+{
+	if (symbol_lookup_table != NULL) {
+		g_hash_table_destroy(symbol_lookup_table);
+		symbol_lookup_table = NULL;
+	}
+
+	if (cause_lookup != NULL) {
+		g_hash_table_destroy(cause_lookup);
+		cause_lookup = NULL;
+	}
+
+	if (causes_array != NULL) {
+		g_ptr_array_foreach(causes_array, (GFunc)free_cause, NULL);
+		g_ptr_array_free(causes_array, TRUE);
+		causes_array = NULL;
+		causes_array_len = 0;
+	}
+
+	if (dtrans != NULL) {
+		g_free(dtrans);
+		dtrans = NULL;
+	}
+}
