blob: 559f1e244a7a7c3dbaafab380e8804e387cc9705 [file] [log] [blame]
/*
* Copyright 2016 The Android Open Source Project
*
* Licensed 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.
*/
#include "code_gen/HalHidlCodeGen.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "test/vts/proto/InterfaceSpecificationMessage.pb.h"
#include "VtsCompilerUtils.h"
#include "code_gen/HalCodeGen.h"
using namespace std;
using namespace android;
namespace android {
namespace vts {
const char* const HalHidlCodeGen::kInstanceVariableName = "hw_binder_proxy_";
void HalHidlCodeGen::GenerateCppBodyCallbackFunction(
std::stringstream& cpp_ss, const InterfaceSpecificationMessage& message,
const string& fuzzer_extended_class_name) {
bool first_callback = true;
for (int i = 0; i < message.attribute_size(); i++) {
const VariableSpecificationMessage& attribute = message.attribute(i);
if (attribute.type() != TYPE_FUNCTION_POINTER || !attribute.is_callback()) {
continue;
}
string name =
"vts_callback_" + fuzzer_extended_class_name + "_" + attribute.name();
if (first_callback) {
cpp_ss << "static string callback_socket_name_;" << endl;
first_callback = false;
}
cpp_ss << endl;
cpp_ss << "class " << name << " : public FuzzerCallbackBase {" << endl;
cpp_ss << " public:" << endl;
cpp_ss << " " << name << "(const string& callback_socket_name) {" << endl;
cpp_ss << " callback_socket_name_ = callback_socket_name;" << endl;
cpp_ss << " }" << endl;
int primitive_format_index = 0;
for (const FunctionPointerSpecificationMessage& func_pt_spec :
attribute.function_pointer()) {
const string& callback_name = func_pt_spec.function_name();
// TODO: callback's return value is assumed to be 'void'.
cpp_ss << endl;
cpp_ss << " static ";
bool has_return_value = false;
if (!func_pt_spec.has_return_type() ||
!func_pt_spec.return_type().has_type() ||
func_pt_spec.return_type().type() == TYPE_VOID) {
cpp_ss << "void" << endl;
} else if (func_pt_spec.return_type().type() == TYPE_PREDEFINED) {
cpp_ss << func_pt_spec.return_type().predefined_type();
has_return_value = true;
} else {
cerr << __func__ << ":" << __LINE__ << " ERROR unknown type "
<< func_pt_spec.return_type().type() << endl;
exit(-1);
}
cpp_ss << " " << callback_name << "(";
int primitive_type_index;
primitive_type_index = 0;
for (const auto& arg : func_pt_spec.arg()) {
if (primitive_type_index != 0) {
cpp_ss << ", ";
}
if (arg.is_const()) {
cpp_ss << "const ";
}
if (arg.type() == TYPE_SCALAR) {
/*
if (arg.scalar_type() == "pointer") {
cpp_ss << definition.aggregate_value(
primitive_format_index).primitive_name(primitive_type_index)
<< " ";
} */
if (arg.scalar_type() == "char_pointer") {
cpp_ss << "char* ";
} else if (arg.scalar_type() == "int32_t" ||
arg.scalar_type() == "uint32_t" ||
arg.scalar_type() == "size_t" ||
arg.scalar_type() == "int64_t" ||
arg.scalar_type() == "uint64_t") {
cpp_ss << arg.scalar_type() << " ";
} else if (arg.scalar_type() == "void_pointer") {
cpp_ss << "void*";
} else {
cerr << __func__ << " unsupported scalar type " << arg.scalar_type()
<< endl;
exit(-1);
}
} else if (arg.type() == TYPE_PREDEFINED) {
cpp_ss << arg.predefined_type() << " ";
} else {
cerr << __func__ << " unsupported type" << endl;
exit(-1);
}
cpp_ss << "arg" << primitive_type_index;
primitive_type_index++;
}
cpp_ss << ") {" << endl;
#if USE_VAARGS
cpp_ss << " const char fmt[] = \""
<< definition.primitive_format(primitive_format_index) << "\";"
<< endl;
cpp_ss << " va_list argp;" << endl;
cpp_ss << " const char* p;" << endl;
cpp_ss << " int i;" << endl;
cpp_ss << " char* s;" << endl;
cpp_ss << " char fmtbuf[256];" << endl;
cpp_ss << endl;
cpp_ss << " va_start(argp, fmt);" << endl;
cpp_ss << endl;
cpp_ss << " for (p = fmt; *p != '\\0'; p++) {" << endl;
cpp_ss << " if (*p != '%') {" << endl;
cpp_ss << " putchar(*p);" << endl;
cpp_ss << " continue;" << endl;
cpp_ss << " }" << endl;
cpp_ss << " switch (*++p) {" << endl;
cpp_ss << " case 'c':" << endl;
cpp_ss << " i = va_arg(argp, int);" << endl;
cpp_ss << " putchar(i);" << endl;
cpp_ss << " break;" << endl;
cpp_ss << " case 'd':" << endl;
cpp_ss << " i = va_arg(argp, int);" << endl;
cpp_ss << " s = itoa(i, fmtbuf, 10);" << endl;
cpp_ss << " fputs(s, stdout);" << endl;
cpp_ss << " break;" << endl;
cpp_ss << " case 's':" << endl;
cpp_ss << " s = va_arg(argp, char *);" << endl;
cpp_ss << " fputs(s, stdout);" << endl;
cpp_ss << " break;" << endl;
// cpp_ss << " case 'p':
cpp_ss << " case '%':" << endl;
cpp_ss << " putchar('%');" << endl;
cpp_ss << " break;" << endl;
cpp_ss << " }" << endl;
cpp_ss << " }" << endl;
cpp_ss << " va_end(argp);" << endl;
#endif
// TODO: check whether bytes is set and handle properly if not.
cpp_ss << " AndroidSystemCallbackRequestMessage callback_message;"
<< endl;
cpp_ss << " callback_message.set_id(GetCallbackID(\"" << callback_name
<< "\"));" << endl;
cpp_ss << " RpcCallToAgent(callback_message, callback_socket_name_);"
<< endl;
if (has_return_value) {
// TODO: consider actual return type.
cpp_ss << " return NULL;";
}
cpp_ss << " }" << endl;
cpp_ss << endl;
primitive_format_index++;
}
cpp_ss << endl;
cpp_ss << " private:" << endl;
cpp_ss << "};" << endl;
cpp_ss << endl;
}
}
void HalHidlCodeGen::GenerateCppBodySyncCallbackFunction(
std::stringstream& cpp_ss, const InterfaceSpecificationMessage& message,
const string& fuzzer_extended_class_name) {
for (auto const& api : message.api()) {
if (api.return_type_hidl_size() > 0) {
cpp_ss << "static void " << fuzzer_extended_class_name << api.name() << "_cb_func("
<< api.return_type_hidl(0).scalar_type() << " arg) {" << endl;
// TODO: support other non-scalar type and multiple args.
cpp_ss << " cout << \"callback " << api.name() << " called\""
<< " << endl;" << endl;
cpp_ss << "}" << endl;
cpp_ss << "std::function<"
<< "void(" << api.return_type_hidl(0).scalar_type() << ")> "
<< fuzzer_extended_class_name << api.name() << "_cb = "
<< fuzzer_extended_class_name << api.name() << "_cb_func;" << endl;
cpp_ss << endl << endl;
}
}
}
void HalHidlCodeGen::GenerateCppBodyFuzzFunction(
std::stringstream& cpp_ss, const InterfaceSpecificationMessage& message,
const string& fuzzer_extended_class_name) {
GenerateCppBodySyncCallbackFunction(
cpp_ss, message, fuzzer_extended_class_name);
for (auto const& sub_struct : message.sub_struct()) {
GenerateCppBodyFuzzFunction(cpp_ss, sub_struct, fuzzer_extended_class_name,
message.original_data_structure_name(),
sub_struct.is_pointer() ? "->" : ".");
}
cpp_ss << "bool " << fuzzer_extended_class_name << "::GetService() {" << endl;
cpp_ss << " hidl_version version = make_hidl_version("
<< (int)message.component_type_version() << ","
<< (int)((message.component_type_version() -
(int)message.component_type_version()) * 10) << ");" << endl;
cpp_ss << " static bool initialized = false;" << endl;
cpp_ss << " if (!initialized) {" << endl;
cpp_ss << " cout << \"[agent:hal] HIDL getService\" << endl;" << endl;
cpp_ss << " status_t status = getService(String16(\""
<< message.package().substr(message.package().find_last_of(".") + 1)
<< "\"), version," << endl;
cpp_ss << " &hw_binder_proxy_);" << endl;
cpp_ss << " cout << \"[agent:hal] HIDL getService status \""
<< " << status << endl;" << endl;
cpp_ss << " initialized = true;" << endl;
cpp_ss << " }" << endl;
cpp_ss << " return true;" << endl;
cpp_ss << "}" << endl << endl;
cpp_ss << "bool " << fuzzer_extended_class_name << "::Fuzz(" << endl;
cpp_ss << " FunctionSpecificationMessage* func_msg," << endl;
cpp_ss << " void** result, const string& callback_socket_name) {" << endl;
cpp_ss << " const char* func_name = func_msg->name().c_str();" << endl;
cpp_ss
<< " cout << \"Function: \" << __func__ << \" \" << func_name << endl;"
<< endl;
// to call another function if it's for a sub_struct
if (message.sub_struct().size() > 0) {
cpp_ss << " if (func_msg->parent_path().length() > 0) {" << endl;
for (auto const& sub_struct : message.sub_struct()) {
GenerateSubStructFuzzFunctionCall(cpp_ss, sub_struct, "");
}
cpp_ss << " }" << endl;
}
for (auto const& api : message.api()) {
cpp_ss << " if (!strcmp(func_name, \"" << api.name() << "\")) {" << endl;
// args - definition;
int arg_count = 0;
for (auto const& arg : api.arg()) {
if (arg.is_callback()) { // arg.type() isn't always TYPE_FUNCTION_POINTER
string name = "vts_callback_" + fuzzer_extended_class_name + "_" +
arg.predefined_type(); // TODO - check to make sure name
// is always correct
if (name.back() == '*') name.pop_back();
cpp_ss << " " << name << "* arg" << arg_count << "callback = new ";
cpp_ss << name << "(callback_socket_name);" << endl;
cpp_ss << " arg" << arg_count << "callback->Register(func_msg->arg("
<< arg_count << "));" << endl;
cpp_ss << " " << GetCppVariableType(arg, &message) << " ";
cpp_ss << "arg" << arg_count << " = (" << GetCppVariableType(arg, &message)
<< ") malloc(sizeof(" << GetCppVariableType(arg, &message) << "));"
<< endl;
// TODO: think about how to free the malloced callback data structure.
// find the spec.
bool found = false;
cout << name << endl;
for (auto const& attribute : message.attribute()) {
if (attribute.type() == TYPE_FUNCTION_POINTER &&
attribute.is_callback()) {
string target_name = "vts_callback_" + fuzzer_extended_class_name +
"_" + attribute.name();
cout << "compare" << endl;
cout << target_name << endl;
if (name == target_name) {
if (attribute.function_pointer_size() > 1) {
for (auto const& func_pt : attribute.function_pointer()) {
cpp_ss << " arg" << arg_count << "->"
<< func_pt.function_name() << " = arg" << arg_count
<< "callback->" << func_pt.function_name() << ";"
<< endl;
}
} else {
cpp_ss << " arg" << arg_count << " = arg" << arg_count
<< "callback->" << attribute.name() << ";" << endl;
}
found = true;
break;
}
}
}
if (!found) {
cerr << __func__ << " ERROR callback definition missing for " << name
<< " of " << api.name() << endl;
exit(-1);
}
} else {
if (arg.type() == TYPE_VECTOR) {
cpp_ss << " " << arg.vector_value(0).scalar_type() << "* "
<< "arg" << arg_count << "buffer = (" << arg.vector_value(0).scalar_type()
<< "*) malloc(8 * sizeof(" << arg.vector_value(0).scalar_type() << "));"
<< endl;
}
if (arg.type() == TYPE_HIDL_CALLBACK) {
cpp_ss << " sp<" << arg.predefined_type() << "> arg" << arg_count << "= nullptr" << endl;
} else if (arg.type() == TYPE_STRUCT) {
cpp_ss << " " << GetCppVariableType(arg, &message) << " ";
cpp_ss << "arg" << arg_count;
} else {
cpp_ss << " " << GetCppVariableType(arg, &message) << " ";
cpp_ss << "arg" << arg_count << " = ";
}
if (arg.type() != TYPE_VECTOR &&
arg.type() != TYPE_HIDL_CALLBACK &&
arg.type() != TYPE_STRUCT) {
std::stringstream msg_ss;
msg_ss << "func_msg->arg(" << arg_count << ")";
string msg = msg_ss.str();
if (arg.type() == TYPE_SCALAR) {
cpp_ss << "(" << msg << ".type() == TYPE_SCALAR)? ";
if (arg.scalar_type() == "pointer" ||
arg.scalar_type() == "pointer_pointer" ||
arg.scalar_type() == "char_pointer" ||
arg.scalar_type() == "void_pointer" ||
arg.scalar_type() == "function_pointer") {
cpp_ss << "reinterpret_cast<" << GetCppVariableType(arg, &message) << ">";
}
cpp_ss << "(" << msg << ".scalar_value()";
if (arg.scalar_type() == "int32_t" ||
arg.scalar_type() == "uint32_t" ||
arg.scalar_type() == "int64_t" ||
arg.scalar_type() == "uint64_t" ||
arg.scalar_type() == "int16_t" ||
arg.scalar_type() == "uint16_t" ||
arg.scalar_type() == "int8_t" ||
arg.scalar_type() == "uint8_t" ||
arg.scalar_type() == "float_t" ||
arg.scalar_type() == "double_t") {
cpp_ss << "." << arg.scalar_type() << "() ";
} else if (arg.scalar_type() == "pointer" ||
arg.scalar_type() == "char_pointer" ||
arg.scalar_type() == "void_pointer") {
cpp_ss << ".pointer() ";
} else {
cerr << __func__ << " ERROR unsupported scalar type "
<< arg.scalar_type() << endl;
exit(-1);
}
cpp_ss << ") : ";
} else if (arg.type() == TYPE_ENUM) {
// TODO: impl
} else if (arg.type() == TYPE_STRUCT) {
// TODO: impl
} else {
cerr << __func__ << ":" << __LINE__ << " unknown type "
<< arg.type() << endl;
exit(-1);
}
cpp_ss << "( (" << msg << ".type() == TYPE_PREDEFINED || " << msg
<< ".type() == TYPE_STRUCT || " << msg
<< ".type() == TYPE_SCALAR)? ";
cpp_ss << GetCppInstanceType(arg, msg, &message);
cpp_ss << " : " << GetCppInstanceType(arg, string(), &message) << " )";
// TODO: use the given message and call a lib function which converts
// a message to a C/C++ struct.
} else if (arg.type() == TYPE_VECTOR) {
// TODO: dynamically generate the initial value for hidl_vec
cpp_ss << "{arg" << arg_count << "buffer, 8}";
}
cpp_ss << ";" << endl;
}
arg_count++;
}
// actual function call
GenerateCodeToStartMeasurement(cpp_ss);
// may need to check whether the function is actually defined.
cpp_ss << " cout << \"ok. let's call.\" << endl;" << endl;
cpp_ss << " *result = const_cast<void*>(reinterpret_cast<const void*>(new string("
<< kInstanceVariableName << "->" << api.name() << "(";
if (arg_count > 0) cpp_ss << endl;
for (int index = 0; index < arg_count; index++) {
cpp_ss << " arg" << index;
if (index != (arg_count - 1)) {
cpp_ss << "," << endl;
}
}
if (api.return_type_hidl_size() > 0) {
// TODO: support callback as the last arg
if (arg_count != 0) cpp_ss << ", ";
cpp_ss << fuzzer_extended_class_name << api.name() << "_cb";
// TODO: callback shall update *result (barrier here?)
// cpp_ss << "*result = ...";
}
cpp_ss << ").toString8().string())));" << endl;
GenerateCodeToStopMeasurement(cpp_ss);
cpp_ss << " cout << \"called\" << endl;" << endl;
// Copy the output (call by pointer or reference cases).
arg_count = 0;
for (auto const& arg : api.arg()) {
if (arg.is_output()) {
// TODO check the return value
cpp_ss << " " << GetConversionToProtobufFunctionName(arg) << "(arg"
<< arg_count << ", "
<< "func_msg->mutable_arg(" << arg_count << "));" << endl;
}
arg_count++;
}
cpp_ss << " return true;" << endl;
cpp_ss << " }" << endl;
}
// TODO: if there were pointers, free them.
cpp_ss << " return false;" << endl;
cpp_ss << "}" << endl;
}
void HalHidlCodeGen::GenerateCppBodyFuzzFunction(
std::stringstream& cpp_ss, const StructSpecificationMessage& message,
const string& fuzzer_extended_class_name,
const string& original_data_structure_name, const string& parent_path) {
for (auto const& sub_struct : message.sub_struct()) {
GenerateCppBodyFuzzFunction(
cpp_ss, sub_struct, fuzzer_extended_class_name,
original_data_structure_name,
parent_path + message.name() + (sub_struct.is_pointer() ? "->" : "."));
}
string parent_path_printable(parent_path);
ReplaceSubString(parent_path_printable, "->", "_");
replace(parent_path_printable.begin(), parent_path_printable.end(), '.', '_');
cpp_ss << "bool " << fuzzer_extended_class_name << "::Fuzz_"
<< parent_path_printable + message.name() << "(" << endl;
cpp_ss << " FunctionSpecificationMessage* func_msg," << endl;
cpp_ss << " void** result, const string& callback_socket_name) {" << endl;
cpp_ss << " const char* func_name = func_msg->name().c_str();" << endl;
cpp_ss
<< " cout << \"Function: \" << __func__ << \" \" << func_name << endl;"
<< endl;
bool is_open;
for (auto const& api : message.api()) {
is_open = false;
if ((parent_path_printable + message.name()) == "_common_methods" &&
api.name() == "open") {
is_open = true;
}
cpp_ss << " if (!strcmp(func_name, \"" << api.name() << "\")) {" << endl;
// args - definition;
int arg_count = 0;
for (auto const& arg : api.arg()) {
cpp_ss << " " << GetCppVariableType(arg) << " ";
cpp_ss << "arg" << arg_count << " = ";
{
std::stringstream msg_ss;
msg_ss << "func_msg->arg(" << arg_count << ")";
string msg = msg_ss.str();
if (arg.type() == TYPE_SCALAR) {
cpp_ss << "(" << msg << ".type() == TYPE_SCALAR && " << msg
<< ".scalar_value()";
if (arg.scalar_type() == "pointer" ||
arg.scalar_type() == "char_pointer" ||
arg.scalar_type() == "void_pointer" ||
arg.scalar_type() == "function_pointer") {
cpp_ss << ".has_pointer())? ";
cpp_ss << "reinterpret_cast<" << GetCppVariableType(arg) << ">";
} else {
cpp_ss << ".has_" << arg.scalar_type() << "())? ";
}
cpp_ss << "(" << msg << ".scalar_value()";
if (arg.scalar_type() == "int32_t" ||
arg.scalar_type() == "uint32_t" ||
arg.scalar_type() == "int64_t" ||
arg.scalar_type() == "uint64_t" ||
arg.scalar_type() == "int16_t" ||
arg.scalar_type() == "uint16_t" ||
arg.scalar_type() == "int8_t" || arg.scalar_type() == "uint8_t" ||
arg.scalar_type() == "float_t" ||
arg.scalar_type() == "double_t") {
cpp_ss << "." << arg.scalar_type() << "() ";
} else if (arg.scalar_type() == "pointer" ||
arg.scalar_type() == "char_pointer" ||
arg.scalar_type() == "function_pointer" ||
arg.scalar_type() == "void_pointer") {
cpp_ss << ".pointer() ";
} else {
cerr << __func__ << " ERROR unsupported type " << arg.scalar_type()
<< endl;
exit(-1);
}
cpp_ss << ") : ";
}
cpp_ss << "( (" << msg << ".type() == TYPE_PREDEFINED || " << msg
<< ".type() == TYPE_STRUCT || " << msg
<< ".type() == TYPE_SCALAR)? ";
cpp_ss << GetCppInstanceType(arg, msg);
cpp_ss << " : " << GetCppInstanceType(arg, string()) << " )";
// TODO: use the given message and call a lib function which converts
// a message to a C/C++ struct.
}
cpp_ss << ";" << endl;
arg_count++;
}
// actual function call
GenerateCodeToStartMeasurement(cpp_ss);
cpp_ss << " cout << \"ok. let's call.\" << endl;" << endl;
cpp_ss << " *result = const_cast<void*>(reinterpret_cast<const void*>(new string("
<< kInstanceVariableName << "->" << parent_path << message.name()
<< "->" << api.name() << "(";
if (arg_count > 0) cpp_ss << endl;
for (int index = 0; index < arg_count; index++) {
cpp_ss << " arg" << index;
if (index != (arg_count - 1)) {
cpp_ss << "," << endl;
}
}
if (api.return_type_hidl_size() > 0) {
if (arg_count != 0) cpp_ss << ", ";
cpp_ss << fuzzer_extended_class_name << api.name() << "_cb";
// TODO: support callback as the last arg and setup barrier here to
// do *result = ...;
}
cpp_ss << ").toString8().string())));" << endl;
GenerateCodeToStopMeasurement(cpp_ss);
cpp_ss << " cout << \"called\" << endl;" << endl;
// Copy the output (call by pointer or reference cases).
arg_count = 0;
for (auto const& arg : api.arg()) {
if (arg.is_output()) {
// TODO check the return value
cpp_ss << " " << GetConversionToProtobufFunctionName(arg) << "(arg"
<< arg_count << ", "
<< "func_msg->mutable_arg(" << arg_count << "));" << endl;
}
arg_count++;
}
cpp_ss << " return true;" << endl;
cpp_ss << " }" << endl;
}
// TODO: if there were pointers, free them.
cpp_ss << " return false;" << endl;
cpp_ss << "}" << endl;
}
void HalHidlCodeGen::GenerateHeaderGlobalFunctionDeclarations(
std::stringstream& h_ss, const string& function_prototype) {
h_ss << "extern \"C\" {" << endl;
h_ss << "extern " << function_prototype << ";" << endl;
h_ss << "}" << endl;
}
void HalHidlCodeGen::GenerateCppBodyGlobalFunctions(
std::stringstream& cpp_ss, const string& function_prototype,
const string& fuzzer_extended_class_name) {
cpp_ss << "extern \"C\" {" << endl;
cpp_ss << function_prototype << " {" << endl;
cpp_ss << " return (android::vts::FuzzerBase*) "
<< "new android::vts::" << fuzzer_extended_class_name << "();" << endl;
cpp_ss << "}" << endl << endl;
cpp_ss << "}" << endl;
}
void HalHidlCodeGen::GenerateSubStructFuzzFunctionCall(
std::stringstream& cpp_ss, const StructSpecificationMessage& message,
const string& parent_path) {
string current_path(parent_path);
if (current_path.length() > 0) {
current_path += ".";
}
current_path += message.name();
string current_path_printable(current_path);
replace(current_path_printable.begin(), current_path_printable.end(), '.',
'_');
cpp_ss << " if (func_msg->parent_path() == \"" << current_path << "\") {"
<< endl;
cpp_ss << " return Fuzz__" << current_path_printable
<< "(func_msg, result, callback_socket_name);" << endl;
cpp_ss << " }" << endl;
for (auto const& sub_struct : message.sub_struct()) {
GenerateSubStructFuzzFunctionCall(cpp_ss, sub_struct, current_path);
}
}
} // namespace vts
} // namespace android