blob: 4349a48743e6041893ceab19fd97e2b0f404e0dd [file]
/* ***************************************************************************
* Copyright (c) 2013-2019 Google, Inc. All rights reserved.
* ***************************************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "drltrace.h"
#include <algorithm>
/****************************************************************************
* Routines to support libcalls hashtable
*/
#define LIBCALLS_TABLE_HASH_BITS 6
/* We init the following hashtable to be able to get library call arguments when
* it is required for printing.
*/
static hashtable_t libcalls_table;
static void
free_args_list(void *p)
{
std::vector<drsys_arg_t *> *args_list = (std::vector<drsys_arg_t *> *) p;
std::vector<drsys_arg_t *>::iterator it;
for (it = args_list->begin(); it != args_list->end(); ++it)
global_free(*it, sizeof(drsys_arg_t), HEAPSTAT_MISC);
delete args_list;
}
static void
init_libcalls_hashtable()
{
hashtable_init_ex(&libcalls_table, LIBCALLS_TABLE_HASH_BITS, HASH_STRING_NOCASE,
false/*!strdup*/, false, free_args_list, NULL, NULL);
}
void
libcalls_hashtable_delete()
{
hashtable_delete(&libcalls_table);
}
std::vector<drsys_arg_t *> *
libcalls_search(const char *name)
{
return (std::vector<drsys_arg_t *> *)hashtable_lookup(&libcalls_table, (void *)name);
}
static bool
libcalls_hashtable_insert(const char *name, std::vector<drsys_arg_t *> *args_list)
{
return hashtable_add(&libcalls_table, (void *)name, (void *)args_list);
}
/****************************************************************************
* Config file parsing routines
*/
static std::string
erase_token(std::string src_string, std::string pattern)
{
std::string::size_type i = src_string.find(pattern);
while (i != std::string::npos) {
src_string.erase(i, pattern.length());
i = src_string.find(pattern, i);
}
return src_string;
}
/* The function returns a new drsys_arg_t object allocated on the global heap
* that the caller should free.
*/
static drsys_arg_t *
config_parse_type(std::string type_name, uint index)
{
ASSERT(!type_name.empty(), "an empty type name was provided");
drsys_arg_t *arg = (drsys_arg_t *)global_alloc(sizeof(drsys_arg_t), HEAPSTAT_MISC);
/* init arg */
arg->type = DRSYS_TYPE_UNKNOWN;
arg->ordinal = index;
arg->size = 0;
arg->arg_name = NULL;
arg->type_name = NULL;
arg->pre = true;
if (type_name.find("__inout") != std::string::npos)
arg->mode = (drsys_param_mode_t)(DRSYS_PARAM_IN|DRSYS_PARAM_OUT);
else if (type_name.find("__out") != std::string::npos)
arg->mode = DRSYS_PARAM_OUT;
else if (type_name.find("*") != std::string::npos)
arg->mode = DRSYS_PARAM_IN;
else
arg->mode = DRSYS_PARAM_INLINED;
/* we don't need special symbols __inout, __out or * further */
type_name = erase_token(type_name, "*");
type_name = erase_token(type_name, "__inout");
type_name = erase_token(type_name, "__out");
/* sanitize input type, we assume ASCII chars here */
std::transform(type_name.begin(), type_name.end(), type_name.begin(), ::toupper);
type_name.erase(std::remove(type_name.begin(), type_name.end(), ' '),
type_name.end());
type_name.erase(std::remove(type_name.begin(), type_name.end(), '\r'),
type_name.end());
type_name.erase(std::remove(type_name.begin(), type_name.end(), '\n'),
type_name.end());
/* FIXME i#1948: Currently, we have only few cross-platform libcalls in the config
* file which is possible to use both for Windows and Linux. However, we
* need to separate them into two configs and fix CMAKE accordingly.
* Moreover, we have to provide two different interfaces for type parsing
* in Windows and Linux.
*/
if (type_name.compare("VOID") == 0) {
arg->type_name = "void";
arg->type = DRSYS_TYPE_VOID;
} else if (type_name.compare("INT") == 0) {
arg->type_name = "int";
arg->size = sizeof(int);
arg->type = DRSYS_TYPE_SIGNED_INT;
} else if (type_name.compare("LONG") == 0) {
arg->type_name = "long";
arg->size = sizeof(long);
arg->type = DRSYS_TYPE_SIGNED_INT;
} else if (type_name.compare("SIZE_T") == 0) {
arg->type_name = "size_t";
arg->size = sizeof(size_t);
arg->type = DRSYS_TYPE_SIZE_T;
}
#ifdef WINDOWS
else if (type_name.compare("HANDLE") == 0) {
arg->type_name = "HANDLE";
arg->size = sizeof(HANDLE);
arg->type = DRSYS_TYPE_HANDLE;
} else if (type_name.compare("HKEY") == 0) {
arg->type_name = "HKEY";
arg->size = sizeof(HKEY);
arg->type = DRSYS_TYPE_HANDLE;
} else if (type_name.compare("HDC") == 0) {
arg->type_name = "HDC";
arg->size = sizeof(HDC);
arg->type = DRSYS_TYPE_HANDLE;
} else if (type_name.compare("HFILE") == 0) {
arg->type_name = "HFILE";
arg->size = sizeof(HFILE);
arg->type = DRSYS_TYPE_HFILE;
} else if (type_name.compare("HMODULE") == 0) {
arg->type_name = "HMODULE";
arg->size = sizeof(HFILE);
arg->type = DRSYS_TYPE_HMODULE;
} else if (type_name.compare("UINT") == 0) {
arg->type_name = "uint";
arg->size = sizeof(UINT);
arg->type = DRSYS_TYPE_UNSIGNED_INT;
} else if (type_name.compare("ULONG") == 0) {
arg->type_name = "ULONG";
arg->size = sizeof(ULONG);
arg->type = DRSYS_TYPE_UNSIGNED_INT;
} else if (type_name.compare("ULONGLONG") == 0) {
arg->type_name = "ULONGLONG";
arg->size = sizeof(ULONGLONG);
arg->type = DRSYS_TYPE_UNSIGNED_INT;
} else if (type_name.compare("DWORD") == 0) {
arg->type_name = "DWORD";
arg->size = sizeof(DWORD);
arg->type = DRSYS_TYPE_UNSIGNED_INT;
} else if (type_name.compare("WORD") == 0) {
arg->type_name = "WORD";
arg->size = sizeof(WORD);
arg->type = DRSYS_TYPE_UNSIGNED_INT;
} else if (type_name.compare("BYTE") == 0) {
arg->type_name = "BYTE";
arg->size = sizeof(BYTE);
arg->type = DRSYS_TYPE_UNSIGNED_INT;
} else if (type_name.compare("BOOL") == 0) {
arg->type_name = "BOOL";
arg->size = sizeof(BOOL);
arg->type = DRSYS_TYPE_BOOL;
} else if (type_name.compare("LCID") == 0) {
arg->type_name = "LCID";
arg->size = sizeof(LCID);
arg->type = DRSYS_TYPE_LCID;
} else if (type_name.compare("LPARAM") == 0) {
arg->type_name = "LPARAM";
arg->size = sizeof(LPARAM);
arg->type = DRSYS_TYPE_LPARAM;
}
#endif
else if (type_name.compare("CHAR") == 0) {
arg->type_name = "char";
arg->type = DRSYS_TYPE_CSTRING;
} else if (type_name.compare("WCHAR") == 0) {
arg->type_name = "wchar_t";
arg->type = DRSYS_TYPE_CWSTRING;
} else {
/* XXX i#1948: We have to extend a list of supported types here. */
arg->type_name = "<unknown>";
DO_ONCE(VNOTIFY(0, "<Found unknown types in the config file>" NL););
VNOTIFY(2, "Found unknown type %s in the config file" NL, type_name.c_str());
}
return arg;
}
static int
split(const char *buf, const char delim, std::vector<std::string> *tokens_list)
{
int count = 0;
std::stringstream ss;
std::string item;
ss.str(buf);
while (std::getline(ss, item, delim)) {
tokens_list->push_back(item);
count++;
}
return count;
}
static bool
parse_line(const char *line, int line_num)
{
std::vector<std::string> tokens;
drsys_arg_t *tmp_arg;
const char *func_name = NULL;
int elem_index = 0, tokens_count = 0;
if (line == NULL)
return false;
if (line[0] == '#') /* just a comment */
return true;
if (strlen(line) <= 0 || line[0] == '\n' || line[0] == '\r')
return true; /* just an empty line */
tokens_count = split(line, '|', &tokens);
if (tokens_count <= 0) {
VNOTIFY(0, "unable to parse config file at line %d: %s" NL, line_num, line);
return false;
}
std::vector<drsys_arg_t *> *args_vector = new std::vector<drsys_arg_t *>();
std::vector<std::string>::iterator it;
for (it = tokens.begin(); it != tokens.end(); ++it) {
/* FIXME i#1948: Currently, we don't support ret value printing and
* skipping it here.
*/
if (elem_index >= 2) {
tmp_arg = config_parse_type(*it, elem_index - 2);
args_vector->push_back(tmp_arg);
} else if (elem_index == 1)
func_name = it->c_str();
elem_index++;
}
if (func_name == NULL || args_vector->size() <= 0) {
VNOTIFY(0, "unable to parse config file at line %d: %s" NL, line_num, line);
return false;
}
VNOTIFY(2, "adding %s from config file with %d arguments in the hashtable" NL,
func_name, args_vector->size());
IF_DEBUG(bool ok =)
libcalls_hashtable_insert(strdup(func_name), args_vector);
ASSERT(ok, "failed to add libcall in the hashtable");
return true;
}
void
parse_config(void)
{
void *map = NULL;
uint64 size_to_read = 0;
size_t actual_size = 0;
file_t file_desc = INVALID_FILE;
int lines_count = 0, line_num = 1;
bool res = false;
std::vector<std::string> lines_list;
if (!op_use_config.get_value())
return;
/* open and map config file */
file_desc = dr_open_file(op_config_file.get_value().c_str(), DR_FILE_READ);
if (file_desc != INVALID_FILE) {
res = dr_file_size(file_desc, &size_to_read);
if (res) {
actual_size = (size_t)size_to_read;
map = dr_map_file(file_desc, &actual_size, 0,
NULL, DR_MEMPROT_READ, 0);
}
}
if (!res || map == NULL || actual_size < size_to_read) {
if (map != NULL)
dr_unmap_file(map, actual_size);
if (file_desc != INVALID_FILE)
dr_close_file(file_desc);
VNOTIFY(0, "unable to open config file at %s, config is not used" NL,
op_config_file.get_value().c_str());
op_use_config.set_value(false);
return;
}
lines_count = split((const char *)map, '\n', &lines_list); /* split buffer by lines */
dr_unmap_file(map, actual_size);
dr_close_file(file_desc);
if (lines_count <= 0) {
VNOTIFY(0, "An empty config file was specified, config is not used" NL);
op_use_config.set_value(false);
return;
}
init_libcalls_hashtable();
std::vector<std::string>::iterator it;
for (it = lines_list.begin(); it != lines_list.end(); it++) {
/* XXX: we have to describe a format of the config file in the drltrace's
* documentation as well as list supported types.
*/
if (!parse_line(it->c_str(), line_num)) {
VNOTIFY(0, "incorrect format for the line %d: %s in config file" NL,
line_num, it->c_str());
op_use_config.set_value(false);
libcalls_hashtable_delete();
break;
}
line_num++;
}
}