| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <sstream> |
| |
| #include "tools/fidlcat/lib/code_generator/test_generator.h" |
| |
| namespace fidlcat { |
| |
| void CodeGenerator::GenerateIncludes(fidl_codec::PrettyPrinter& printer) { |
| printer << "#include <lib/async-loop/cpp/loop.h>\n"; |
| printer << "#include <lib/async-loop/default.h>\n"; |
| printer << "#include <lib/async/default.h>\n"; |
| printer << "#include <lib/syslog/cpp/macros.h>\n"; |
| printer << '\n'; |
| printer << "#include <gtest/gtest.h>\n"; |
| printer << '\n'; |
| printer << "#include \"lib/sys/cpp/component_context.h\"\n"; |
| printer << '\n'; |
| |
| GenerateFidlIncludes(printer); |
| |
| printer << '\n'; |
| } |
| |
| void CodeGenerator::GenerateFidlIncludes(fidl_codec::PrettyPrinter& printer) { |
| for (const auto& fidl_include : fidl_headers_) { |
| printer << "#include <" << fidl_include << ">\n"; |
| } |
| } |
| |
| std::unique_ptr<FidlCallInfo> OutputEventToFidlCallInfo(OutputEvent* output_event) { |
| const Syscall* syscall = output_event->syscall(); |
| SyscallKind syscall_kind = syscall->kind(); |
| |
| // We are only interested in FIDL calls |
| switch (syscall_kind) { |
| case SyscallKind::kChannelRead: |
| case SyscallKind::kChannelWrite: |
| case SyscallKind::kChannelCall: |
| break; |
| default: |
| return nullptr; |
| } |
| |
| // We inspect the message to extract |
| // "interface name", "method name" and "message content". |
| // Based on the system call, this could be in either of output event or invoked event. |
| const fidl_codec::ProtocolMethod* method = nullptr; |
| zx_txid_t txid; |
| |
| switch (syscall_kind) { |
| case SyscallKind::kChannelRead: |
| method = output_event->GetMessage()->method(); |
| txid = output_event->GetMessage()->txid(); |
| break; |
| case SyscallKind::kChannelWrite: |
| case SyscallKind::kChannelCall: |
| method = output_event->invoked_event()->GetMessage()->method(); |
| txid = output_event->invoked_event()->GetMessage()->txid(); |
| break; |
| default: |
| return nullptr; |
| break; |
| } |
| |
| if (method == nullptr) { |
| // TODO(nimaj): investigate why this happens for zx_channel_read and zx_channel_write |
| // We will not be able to determine method name nor interface name |
| return nullptr; |
| } |
| |
| const fidl_codec::PayloadableValue* decoded_input_value = nullptr; |
| const fidl_codec::PayloadableValue* decoded_output_value = nullptr; |
| |
| switch (syscall_kind) { |
| case SyscallKind::kChannelWrite: |
| decoded_input_value = output_event->invoked_event()->GetMessage()->decoded_request(); |
| if (!decoded_input_value) { |
| // monitored process is a server; event is a response sent by server |
| decoded_input_value = output_event->invoked_event()->GetMessage()->decoded_response(); |
| } |
| break; |
| case SyscallKind::kChannelRead: |
| decoded_output_value = output_event->GetMessage()->decoded_response(); |
| if (!decoded_output_value) { |
| // monitored process is a server; event is a request received by the server |
| decoded_output_value = output_event->GetMessage()->decoded_request(); |
| } |
| break; |
| case SyscallKind::kChannelCall: |
| decoded_input_value = output_event->invoked_event()->GetMessage()->decoded_request(); |
| if (decoded_input_value && output_event->GetMessage() != nullptr) { |
| decoded_output_value = output_event->GetMessage()->decoded_response(); |
| } |
| break; |
| default: |
| return nullptr; |
| break; |
| } |
| |
| // Extract handle information from output event in 2 steps: |
| // (1/2) Find handle's struct member |
| const fidl_codec::StructMember* handle_member = syscall->SearchInlineMember("handle", true); |
| // (2/2) Lookup handle's struct member in invoked_event |
| const fidl_codec::HandleValue* handle = |
| output_event->invoked_event()->GetHandleValue(handle_member); |
| zx_handle_t handle_id = handle->handle().handle; |
| |
| bool crashed = output_event->returned_value() == ZX_ERR_PEER_CLOSED; |
| |
| return std::make_unique<FidlCallInfo>( |
| crashed, method->enclosing_protocol().name(), handle_id, txid, syscall_kind, method->name(), |
| method->request(), method->response(), decoded_input_value, decoded_output_value); |
| } |
| |
| std::string FidlMethodToIncludePath(std::string_view identifier) { |
| std::string result = std::string(identifier.substr(0, identifier.find('/'))); |
| std::replace(result.begin(), result.end(), '.', '/'); |
| result.append("/cpp/fidl.h"); |
| return result; |
| } |
| |
| std::string ToSnakeCase(std::string_view str) { |
| std::ostringstream result; |
| std::string separator = ""; |
| for (char c : str) { |
| if (c == '.' || c == '/') { |
| result << separator; |
| } else if (isupper(c)) { |
| result << separator << static_cast<char>(std::tolower(c)); |
| separator = ""; |
| } else { |
| result << c; |
| separator = "_"; |
| } |
| } |
| return result.str(); |
| } |
| |
| } // namespace fidlcat |