blob: b3772f33b49cbac6d5d41c9b25d6e124e55ffc0a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Intel, Mikhail Y. Fursov
*
*/
//
// Decides whether or not to "accept" a method based on an environment
// variable and an optional file.
//
// Assume FOO is the environment variable that gives the information.
//
// FOO=file:range_list
//
// "file" is the name of the file that contains the list of all
// methods.
//
// If "range_list" is empty, no methods are jitted, and the list of
// methods is printed to "file".
//
// "range_list" contains a list of methods and ranges, separated by
// commas. A single number denotes a one-based index into the method
// list. A range (start-end) denotes a range of indices into the
// method list.
// The string "class::method" denotes a specific method.
// "class::" matches all methods of the given class.
// "method" (not containing a colon) matches that method of
// any class.
// A "-" preceding a list element means to exclude that method(s) from
// the list of methods to jit.
//
// FOO=c:\tmp\mlist: (jit nothing, create a method list file)
// FOO=mlist: (jit nothing, create a file in the current directory)
// FOO=list:1-1000 (jit the first 1000 methods from file "list")
// FOO=java/lang/Object::<init> (jit just a single method)
// FOO=-<init>,-<clinit> (jit all but initializers)
// FOO=list:1-100,-50-60 (jit methods 1-49 and 61-100)
// FOO=(I) (jit only methods with a single int in the signature)
// FOO=(Ljava/lang/Object;)
// FOO=(L;) (jit only methods with a single class ptr in signature)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "VMInterface.h"
#include "Type.h"
#include "methodtable.h"
namespace Jitrino {
#define CLASS_METHOD_SEPARATOR_CHAR ':'
#define DESCRIPTOR_START_CHAR '('
#define DESCRIPTOR_END_CHAR ')'
#define REF_START_CHAR 'L'
#define REF_END_CHAR ';'
#define FILE_CHAR ':'
char* strdup(MemoryManager& mm , const char* str) {
size_t len = strlen(str);
char* newstr = new (mm) char[len+1];
strncpy(newstr, str, len);
newstr[len]='\0';
return newstr;
}
void Method_Table::make_filename(char *str, int len)
{
_method_file = new (_mm) char[1+len];
strncpy(_method_file, str, len);
_method_file[len] = '\0';
}
// Decides whether two signatures match. The strictest test is
// strcmp(), but we are more lenient by allowing "L;" instead of,
// e.g., "Ljava/lang/Object;". Anything following the ')' (i.e.,
// the return value) of either string is ignored.
static bool matching_signature(const char *lenient, const char *exact)
{
int i, j;
int len1 = (int) strlen(lenient);
int len2 = (int) strlen(exact);
for (i=j=0; i<len1 && j<len2; i++,j++)
{
if (lenient[i] != exact[j])
return false;
if (lenient[i] == DESCRIPTOR_END_CHAR)
return true;
if (lenient[i] == REF_START_CHAR)
{
i++; j++;
if (lenient[i] == REF_END_CHAR) // accept any classname
{
while (j < len2 && exact[j] != REF_END_CHAR)
j ++;
}
else
{
while (i < len1 && j < len2 && lenient[i] != REF_END_CHAR)
{
if (lenient[i] != exact[j])
return false;
i++; j++;
}
}
}
}
return (i == len1 && j == len2);
}
// [class::][method][(signature)]
static void parse_method_string(MemoryManager& _mm, char *str, Method_Table::method_record *rec)
{
int i;
int len = (int) strlen(str);
rec->class_name = rec->method_name = rec->signature = NULL;
if (len == 0)
return;
bool is_at_class_method_separator = false;
// Search forward for CLASS_METHOD_SEPARATOR_CHAR or DESCRIPTOR_START_CHAR
for (i=0; i<len; i++)
{
is_at_class_method_separator =
(str[i] == CLASS_METHOD_SEPARATOR_CHAR && str[i+1] == CLASS_METHOD_SEPARATOR_CHAR);
if (is_at_class_method_separator)
{
i ++; // make i point to the last character of the separator
break;
}
if (str[i] == DESCRIPTOR_START_CHAR)
break;
}
if (i >= len) // no class or descriptor
{
if (str[0] != '\0')
rec->method_name = strdup(_mm, str);
return;
}
if (is_at_class_method_separator)
{
// extract the class
rec->class_name = new (_mm) char[i-1 + 1];
strncpy(rec->class_name, str, i-1);
rec->class_name[i-1] = '\0';
// skip ahead to the descriptor
str += (i+1);
len -= (i+1);
for (i=0; i<len; i++)
{
if (str[i] == DESCRIPTOR_START_CHAR)
break;
}
if (i >= len) // no descriptor
{
if (str[0] != '\0')
rec->method_name = strdup(_mm, str);
return;
}
}
// we're at the start of the signature, with the method name preceding
if (i > 0)
{
rec->method_name = new (_mm) char[i+1];
strncpy(rec->method_name, str, i);
rec->method_name[i] = '\0';
}
rec->signature = strdup(_mm, &str[i]);
}
// Returns true on success, false on failure
bool Method_Table::read_method_table()
{
const size_t max_size = 1000;
char buf[max_size];
FILE *file = fopen(_method_file, "r");
if (file == NULL)
{
fprintf(stderr, "Couldn't open method table file %s\n", _method_file);
return false;
}
while (fgets(buf, max_size, file) != NULL)
{
// strip out any newline at the end
int buflen = (int) strlen(buf);
if (buf[buflen-1] == '\n')
buf[buflen-1] = '\0';
if (buf[buflen-2] == '\r') // file generated on NT and used on Linux,
buf[buflen-2] = '\0'; // this case happens
method_record* rec = new (_mm) method_record();
parse_method_string(_mm, buf, rec);
_method_table.push_back(rec);
}
fclose(file);
return true;
}
static bool matches(Method_Table::method_record *test_entry,
const char *class_name, const char *method_name, const char *signature)
{
if (test_entry->class_name != NULL && strcmp(test_entry->class_name, class_name) != 0)
return false;
if (test_entry->method_name != NULL && strcmp(test_entry->method_name, method_name) != 0)
return false;
if (test_entry->signature != NULL && !matching_signature(test_entry->signature, signature))
return false;
return true;
}
void Method_Table::init(const char *default_envvar, const char *envvarname)
{
if (default_envvar == NULL || default_envvar[0] == '\0')
{
return;
}
char *rangestr;
char *envvar = strdup(_mm, default_envvar);
// strip away double-quote characters
if (envvar[0] == '"') {
envvar ++;
}
int evlen = (int) strlen(envvar);
if (evlen > 0 && envvar[evlen-1] == '"') {
envvar[--evlen] = '\0';
}
if (evlen == 0) {
return;
}
int i;
for (i=evlen-1; i>=0; i--)
{
if (FILE_CHAR == CLASS_METHOD_SEPARATOR_CHAR)
{
// If we are looking at FILE_CHAR and the character to the left is not FILE_CHAR,
// then this is a legitimate FILE_CHAR.
if (envvar[i] == FILE_CHAR && (i == 0 || envvar[i-1] != FILE_CHAR))
break;
// If we are looking at FILE_CHAR and the character to the left is also FILE_CHAR,
// then we are actually looking at a double CLASS_METHOD_SEPARATOR_CHAR, which
// we should skip over.
if (envvar[i] == FILE_CHAR && i != 0 && envvar[i-1] == FILE_CHAR)
i --;
} else {
if (envvar[i] == FILE_CHAR)
break;
}
}
if (i == 0) // no legitimate filename given
{
return;
}
else if (i == evlen-1) // filename only, no ranges
{
make_filename(envvar, evlen-1);
_dump_to_file = true;
FILE *file = fopen(_method_file, "w");
if (file == NULL)
{
fprintf(stderr, "Couldn't truncate method table file %s\n",
_method_file);
_dump_to_file = false;
}
else
fclose(file);
return;
}
else if (i >= 0) // filename plus ranges
{
make_filename(envvar, i);
rangestr = &envvar[i+1];
read_method_table();
}
else // no filename, only ranges
{
rangestr = envvar;
}
// parse the ranges in rangestr
for (rangestr=strtok(rangestr, ","); rangestr!=NULL; rangestr=strtok(NULL, ","))
{
int opposite = 0;
int start, end;
if (rangestr[0] == '-')
{
opposite = 1;
rangestr ++;
}
else
{
_default_decision = mt_rejected;
}
if (rangestr[0] >= '0' && rangestr[0] <= '9') {
// look for a range of numbers
sscanf(rangestr, "%d", &start);
end = start;
while (rangestr[0] != '\0' && rangestr[0] != '-') {
rangestr ++;
}
if (rangestr[0] == '-') {
sscanf(rangestr+1, "%d", &end);
}
start --;
if (start < 0) {
start = 0;
}
end --;
for (i=start; i<=end && i<(int)_method_table.size(); i++) {
_method_table[i]->decision = (opposite ? mt_rejected : mt_accepted);
}
} else {
method_record* rec = new (_mm) method_record();
parse_method_string(_mm, rangestr, rec);
rec->decision = (opposite ? mt_rejected : mt_accepted);
_decision_table.push_back(rec);
}
}
// change all "undecided" to default decision
for (i=0; i<(int)_method_table.size(); i++) {
if (_method_table[i]->decision == mt_undecided) {
_method_table[i]->decision = _default_decision;
}
}
}
Method_Table::Method_Table(MemoryManager& memManager,
const char *default_envvar,
const char *envvarname,
bool accept_by_default):
_mm(memManager),
_method_table (_mm),
_decision_table (_mm),
_dump_to_file (false),
_method_file (NULL)
{
_default_decision = accept_by_default ? mt_accepted : mt_rejected;
init(default_envvar, envvarname);
}
bool Method_Table::accept_this_method(MethodDesc &md) {
const char* classname = md.getParentType()->getName();
const char *methodname = md.getName();
const char *signature = md.getSignatureString();
return accept_this_method(classname, methodname, signature);
}
bool Method_Table::accept_this_method(const char* classname, const char *methodname, const char *signature)
{
int i;
if (_dump_to_file)
{
FILE *file = fopen(_method_file, "a");
if (file != NULL)
{
fprintf(file, "%s%c%c%s%s\n", classname, CLASS_METHOD_SEPARATOR_CHAR, CLASS_METHOD_SEPARATOR_CHAR, methodname, signature);
fclose(file);
}
return false;
}
// First look through the decision_table strings.
for (i=0; i<(int)_decision_table.size(); i++)
{
if (matches(_decision_table[i], classname, methodname, signature)) {
return (_decision_table[i]->decision == mt_accepted);
}
}
// Then look through the method table.
for (i=0; i<(int)_method_table.size(); i++) {
if ((_method_table[i]->class_name==NULL || !strcmp(_method_table[i]->class_name, classname)) &&
(_method_table[i]->method_name==NULL || !strcmp(_method_table[i]->method_name, methodname)) &&
(_method_table[i]->signature==NULL || matching_signature(_method_table[i]->signature, signature)))
{
return (_method_table[i]->decision == mt_accepted);
}
}
return (_default_decision == mt_rejected ? false : true);
}
bool Method_Table::is_in_list_generation_mode() {
return _dump_to_file;
}
void Method_Table::add_method_record(const char* className, const char* methodName, const char* signature, Method_Table::Decision decision, bool copyVals) {
method_record* rec = new (_mm) method_record();
if (copyVals) {
size_t len = strlen(className)+1; //+1 == '\0' char
rec->class_name = new (_mm) char[len];
strncpy(rec->class_name, className, len);
len = strlen(methodName)+1; //+1 == '\0' char
rec->method_name= new (_mm) char[len];
strncpy(rec->method_name, methodName, len);
len = strlen(signature)+1; //+1 == '\0' char
rec->signature= new (_mm) char[len];
strncpy(rec->signature, signature, len);
} else {
rec->class_name = (char*)className;
rec->method_name = (char*)methodName;
rec->signature = (char*)signature;
}
rec->decision = decision;
_method_table.push_back(rec);
}
} //namespace Jitrino