/*
* 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
* 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 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 *".
*/
/*
* The dtrace translation rules we get from the script
*/
/*
* These structures are only used inside .trans parser.
*/
typedef struct {
int lt_dm_priority;
char *lt_dm_macro;
} lt_dmacro_t;
typedef struct {
} lt_parser_t;
/* ARGSUSED */
static void
{
}
static void
{
free(d->lt_dm_macro);
free(d);
}
/*
* Add a cause.
*/
static lt_cause_t *
{
return (entry);
}
/*
* Set a cause to "disabled" state.
*/
static void
{
}
}
/*
* Helper functions that reads a line from a character array.
*/
static int
int *index)
{
return (0);
}
return (0);
}
--line_len;
++line;
break;
}
}
*line = '\0';
return (1);
}
/*
* Parse special command from configuration file. Special command
* has the following format :
* disable_cause <cause name>
*/
static int
{
char *tmp;
char old_chr = 0;
/*
* disable_cause FSFlush Daemon
* ^
*/
if (*begin == '\0') {
return (0);
}
++tmp) {
}
*tmp = '\0';
if (old_chr == '\0') {
/* Must have an argument */
"Invalid command format: %s\n",
begin);
return (-1);
}
++begin;
}
} else {
"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
{
int priority = 0;
char *match;
char *match_dup;
char *cause_str;
char *tmp;
/*
* 10 genunix`pread Syscall pread
* ^
*/
return (-1);
}
/*
* 10 genunix`pread Syscall pread
* --^
*/
/* At least one space char after <priority> */
return (-1);
}
++begin;
}
if (*begin == 0) {
return (-1);
}
/*
* 10 genunix`pread Syscall pread
* -----^
*/
++tmp) {
}
if (*tmp == '\0') {
return (-1);
}
*tmp = '\0';
/* Check if we have mapped this function before. */
match_entry = (lt_match_t *)
if (match_entry != NULL &&
/* We already have a higher entry. Ignore this. */
return (0);
}
/*
* 10 genunix`pread Syscall pread
* -------------------------------------^
*/
++begin;
}
if (*begin == 0) {
return (-1);
}
/* Check if we have mapped this cause before. */
cause = (lt_cause_t *)
}
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
{
int priority = 0;
char *entryprobe;
char *returnprobe;
char *cause_str;
/*
* 10 syscall::pread:entry syscall::pread:return Syscall pread
* ^
*/
return (-1);
}
/*
* 10 syscall::pread:entry syscall::pread:return Syscall pread
* --^
*/
++begin;
}
if (*begin == 0) {
return (-1);
}
/*
* 10 syscall::pread:entry syscall::pread:return Syscall pread
* -----^
*/
++tmp) {
}
if (*tmp == '\0') {
return (-1);
}
*tmp = '\0';
entryprobe = begin;
++begin;
}
/*
* 10 syscall::pread:entry syscall::pread:return Syscall pread
* -----------------------------^
*/
++tmp) {
}
if (*tmp == '\0') {
return (-1);
}
*tmp = '\0';
returnprobe = begin;
++begin;
}
/*
* 10 syscall::pread:entry syscall::pread:return Syscall pread
* -----------------------------------------------------^
*/
if (*begin == 0) {
return (-1);
}
/* Check if we have mapped this cause before. */
cause = (lt_cause_t *)
}
dmacro);
}
return (0);
}
/*
* Helper function to collect TRANSLATE() macros.
*/
/* ARGSUSED */
static void
{
}
/*
* 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
{
int len;
int current = 0;
int ret = 0;
char flag;
¤t)) {
lt_display_error("Configuration line too long.\n");
goto err;
}
++begin;
}
if (*begin == '\0') {
/* Ignore empty line */
continue;
}
/* Delete trailing spaces. */
--end;
}
++begin;
switch (flag) {
case '#':
ret = 0;
break;
case ';':
break;
case 'D':
case 'd':
"No space after flag char: %s\n", line);
}
++begin;
}
break;
case 'S':
case 's':
"No space after flag char: %s\n", line);
}
++begin;
}
break;
default:
ret = -1;
break;
}
if (ret != 0) {
"Invalid configuration line: %s\n", line);
goto err;
}
}
}
return (0);
err:
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)
{
int config_loaded_len = 0;
int work_len = 0;
#ifdef EMBED_CONFIGS
#endif
"Unable to open configuration file.\n");
return (-1);
}
/* A zero-byte translation is valid */
if (config_loaded_len != 0 &&
"Unable to read configuration file.\n");
return (-1);
}
(void) printf("Loaded configuration from %s\n",
}
/* 0 is not used, but it is kept as a place for bugs etc. */
return (-1);
}
if (config_loaded != NULL) {
}
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
{
if (cause_lookup == NULL) {
} else {
cause = (lt_cause_t *)
}
char *cause_dup;
if (name[0] == '#') {
}
}
}
/*
* 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
{
if (symbol_lookup_table == NULL) {
return (0);
}
match = (lt_match_t *)
match = (lt_match_t *)
}
}
return (0);
} else {
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 *
{
return (NULL);
}
return (NULL);
} else {
}
}
/*
* Check cause flag.
* If CAUSE_ALL_FLAGS is passed in, all flags are returned.
*/
int
{
return (0);
}
return (0);
} else {
}
}
/*
* Append macros to D script, if any.
*/
int
{
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) {
}
if (cause_lookup != NULL) {
cause_lookup = NULL;
}
if (causes_array != NULL) {
causes_array = NULL;
causes_array_len = 0;
}
}
}