| /*! |
| ############################################################################## |
| # Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved. |
| # |
| # SPDX-License-Identifier: BSD-3-Clause |
| ############################################################################## |
| */ |
| // Implements the trace plugin interface for the MTI interface to trace |
| // source data from Arm FVP. |
| |
| #include "MTI/PluginInterface.h" |
| #include "MTI/PluginFactory.h" |
| #include "MTI/PluginInstance.h" |
| #include "MTI/ModelTraceInterface.h" |
| #include "MTI/ParameterInterface.h" |
| |
| #include "plugin_utils.h" |
| #include "trace_sources.h" |
| |
| #include <errno.h> |
| #include <string> |
| #include <algorithm> |
| #include <cstdio> |
| #include <sstream> |
| #include <vector> |
| #include <map> |
| #include <typeinfo> |
| #include <typeindex> |
| #include <utility> |
| #include "SimpleCADI.h" |
| |
| #ifdef SG_MODEL_BUILD |
| #include "builddata.h" |
| #define PLUGIN_VERSION FULL_VERSION_STRING |
| #else |
| #define PLUGIN_VERSION "unreleased" |
| #endif |
| |
| enum plugin_param_t |
| { |
| TRACE_FILE_PREFIX, |
| TRACE_MODE, |
| TOTAL_PARAMETERS |
| }; |
| using namespace eslapi; |
| using namespace MTI; |
| using namespace std; |
| |
| // Implements the plugin interface for trace coverage |
| |
| // Class used to return a static object CAInterface. CAInterface provides a |
| // basis for a software model built around ’components’ and ’interfaces’. |
| // A component provides concrete implementations of one or more interfaces. |
| // Interfaces are identified by a string name (of type if_name_t), and an |
| // integer revision (type if_rev_t). A higher revision number indicates a newer |
| // revision of the same interface. |
| class ThePluginFactory :public PluginFactory |
| { |
| public: |
| virtual CAInterface *ObtainInterface(if_name_t ifName, |
| if_rev_t minRev, |
| if_rev_t * actualRev); |
| |
| virtual uint32_t GetNumberOfParameters(); |
| |
| virtual eslapi::CADIReturn_t |
| GetParameterInfos(eslapi::CADIParameterInfo_t *parameter_info_list); |
| |
| virtual CAInterface *Instantiate(const char *instance_name, |
| uint32_t number_of_parameters, |
| eslapi::CADIParameterValue_t *parameter_values); |
| |
| virtual void Release(); |
| |
| virtual const char *GetType() const { return "CoverageTrace"; } |
| virtual const char *GetVersion() const { return PLUGIN_VERSION; } |
| }; |
| |
| static ThePluginFactory *GetThePluginFactory(); |
| class CoverageTrace : |
| public ParameterInterface, |
| public PluginInstance |
| { |
| public: |
| virtual CAInterface * ObtainInterface(if_name_t ifName, |
| if_rev_t minRev, |
| if_rev_t * actualRev); |
| |
| CoverageTrace(const char *instance_name, const char *trace_file_prefix, |
| int _mode); |
| ~CoverageTrace(); |
| |
| /** This is to associate a plugin with a simulation instance. Exactly one |
| * simulation must be registered. |
| * */ |
| virtual eslapi::CADIReturn_t RegisterSimulation(eslapi::CAInterface |
| *simulation); |
| |
| // This is called before the plugin .dll/.so is unloaded and should allow |
| // the plugin to do it's cleanup. |
| virtual void Release(); |
| void Save(void); |
| |
| virtual const char *GetName() const; |
| virtual eslapi::CADIReturn_t GetParameterInfos(uint32_t start_index, |
| uint32_t desired_num_of_params, |
| uint32_t* actual_num_of_params, |
| eslapi::CADIParameterInfo_t* params); |
| |
| virtual eslapi::CADIReturn_t GetParameterInfo(const char* parameterName, |
| eslapi::CADIParameterInfo_t* param); |
| |
| virtual eslapi::CADIReturn_t GetParameterValues(uint32_t parameter_count, |
| uint32_t* actual_num_of_params_read, |
| eslapi::CADIParameterValue_t* param_values_out); |
| virtual eslapi::CADIReturn_t SetParameterValues(uint32_t parameter_count, |
| eslapi::CADIParameterValue_t* parameters, |
| eslapi::CADIFactoryErrorMessage_t* error); |
| ModeChangeTraceContext *mode_change; |
| private: |
| std::string instance_name; |
| |
| bool Error(const char *); |
| |
| vector<TraceComponentContext*> trace_components; |
| std::string trace_file_prefix; |
| int trace_mode; |
| }; |
| |
| CAInterface *CoverageTrace::ObtainInterface(if_name_t ifName, |
| if_rev_t minRev, |
| if_rev_t * actualRev) |
| { |
| printf("CoverageTrace::ObtainInterface\n"); |
| // If someone is asking for the matching interface |
| if((strcmp(ifName,PluginInstance::IFNAME()) == 0) && |
| // and the revision of this interface implementation is |
| (minRev <= PluginInstance::IFREVISION())) |
| // at least what is being asked for |
| { |
| if (actualRev) // Make sure this is not a NULL pointer |
| *actualRev = PluginInstance::IFREVISION(); |
| return dynamic_cast<PluginInstance *>(this); |
| } |
| |
| if((strcmp(ifName, CAInterface::IFNAME()) == 0) && |
| minRev <= CAInterface::IFREVISION()) |
| { |
| if (actualRev != NULL) |
| *actualRev = CAInterface::IFREVISION(); |
| return dynamic_cast<CAInterface *>(dynamic_cast<PluginInstance *>(this)); |
| } |
| if((strcmp(ifName, ParameterInterface::IFNAME()) == 0) && |
| minRev <= ParameterInterface::IFREVISION()) |
| { |
| if (actualRev != NULL) |
| *actualRev = ParameterInterface::IFREVISION(); |
| return dynamic_cast<ParameterInterface *>(this); |
| } |
| printf("CoverageTrace::ObtainInterface Failed!\n"); |
| return NULL; |
| } |
| |
| |
| CoverageTrace::CoverageTrace(const char *instance_name_, |
| const char *trace_file_prefix_, int _mode) : |
| instance_name(instance_name_), |
| trace_file_prefix(trace_file_prefix_), |
| trace_mode(_mode) |
| { |
| printf("CoverageTrace::CoverageTrace\n"); |
| } |
| |
| CoverageTrace::~CoverageTrace() |
| { |
| printf("CoverageTrace::~CoverageTrace\n"); |
| } |
| |
| bool |
| CoverageTrace::Error(const char *msg) |
| { |
| fprintf(stderr, "%s\n", msg); |
| return false; |
| } |
| |
| // Method that registers the simulation traces events. In this case registers |
| // for trace sources with the 'INST' name. |
| CADIReturn_t |
| CoverageTrace::RegisterSimulation(CAInterface *ca_interface) |
| { |
| printf("CoverageTrace::RegisterSimulation\n"); |
| if (!ca_interface) { |
| Error("Received CAInterface NULL pointer."); |
| return CADI_STATUS_IllegalArgument; |
| } |
| std::stringstream ss; |
| |
| SystemTraceInterface *sys_if = |
| ca_interface->ObtainPointer<SystemTraceInterface>(); |
| if (sys_if == 0) { |
| Error("Got a NULL SystemTraceInterface."); |
| return CADI_STATUS_GeneralError; |
| } |
| |
| for(SystemTraceInterface::TraceComponentIndex tci=0; |
| tci < sys_if->GetNumOfTraceComponents(); ++tci) { |
| const char* tpath = sys_if->GetComponentTracePath(tci); |
| CAInterface *caif = sys_if->GetComponentTrace(tci); |
| ComponentTraceInterface *cti = |
| caif->ObtainPointer<ComponentTraceInterface>(); |
| InstructionTraceContext *inst_cont = NULL; |
| MTI::EventClass * event = NULL; |
| |
| if (cti == 0) { |
| Error("Could not get TraceInterface for component."); |
| continue; |
| } |
| |
| if (cti->GetTraceSource("INST") != 0) { |
| TraceComponentContext *trace_component = new |
| TraceComponentContext(tpath); |
| |
| // To register a new trace source the arguments are the |
| // name of the trace source followed by a vector of |
| // pairs of (field name,field type). |
| inst_cont = new InstructionTraceContext( |
| "INST", |
| { {"PC", u32}, |
| {"SIZE", u32}} |
| ); |
| inst_cont->nb_insts = 0; |
| event = inst_cont->CreateEvent(&cti, inst_cont->Callback); |
| event->UnregisterCallback(inst_cont->Callback, inst_cont); |
| trace_component->AddTraceSource(inst_cont); |
| trace_components.push_back(trace_component); |
| } |
| if (cti->GetTraceSource("MODE_CHANGE") != 0) { |
| MTI::EventClass * mode_event = NULL; |
| if(!event) { |
| Error("Could not set mode_change event"); |
| continue; |
| } |
| // To register a new trace source the arguments are the |
| // name of the trace source followed by a vector of |
| // pairs of (field name,field type). |
| this->mode_change = new ModeChangeTraceContext( |
| "MODE_CHANGE", |
| { {"MODE", u8}, |
| {"NON_SECURE", u8} |
| } |
| ); |
| mode_event = this->mode_change->CreateEvent(&cti, this->mode_change->Callback); |
| this->mode_change->event = event; |
| this->mode_change->itc = inst_cont; |
| this->mode_change->mode_mask = trace_mode; |
| mode_event->RegisterCallback(this->mode_change->Callback, mode_change); |
| } |
| |
| } |
| |
| return CADI_STATUS_OK; |
| } |
| |
| // This is called before the plugin .dll/.so is unloaded and should allow the |
| // plugin to do it's cleanup. |
| void |
| CoverageTrace::Release() |
| { |
| printf("CoverageTrace::Release\n"); |
| // We can dump our data now |
| this->Save(); |
| } |
| |
| void |
| CoverageTrace::Save(void) |
| { |
| int error = 0; |
| char* fname; |
| int ret; |
| std::vector<TraceComponentContext*>::iterator tcc; |
| for (tcc = trace_components.begin(); tcc < trace_components.end(); ++tcc) { |
| TraceComponentContext *tcont = *tcc; |
| // Print some overall stats |
| InstructionTraceContext* rtc = (InstructionTraceContext*) |
| tcont->trace_sources["INST"]; |
| printf("Trace path: %s\n", tcont->trace_path.c_str()); |
| |
| // Construct a trace file name |
| int status = asprintf(&fname, "%s-%s.log", |
| this->trace_file_prefix.c_str(), |
| tcont->trace_path.c_str()); |
| if ( status == -1) |
| { |
| printf("Error in asprintf: %d\n", status); |
| printf("Error description is : %s\n", strerror(errno)); |
| } |
| |
| // Open it |
| FILE* fp = fopen(fname, "w"); |
| if (fp == NULL) { |
| fprintf(stderr, "Can't open file %s for writing.\n", fname); |
| error = 1; |
| break; |
| } |
| |
| InstStatMap::iterator map_it; |
| // Dump the detailed stats |
| for (map_it = rtc->stats.begin(); map_it != rtc->stats.end(); |
| ++map_it) { |
| fprintf(fp, "%08x %lu %lu\n", (unsigned int)map_it->first, |
| (unsigned long)map_it->second.cnt, |
| (unsigned long)map_it->second.size); |
| } |
| |
| // Close the file |
| ret = fclose(fp); |
| if (ret != 0) { |
| fprintf(stderr, "Failed to close %s: %s.", fname, strerror(errno)); |
| error = 1; |
| break; |
| } |
| |
| free(fname); |
| } |
| printf("Everything saved\n"); |
| if (error != 0) |
| delete this; |
| } |
| |
| const char * |
| CoverageTrace::GetName() const |
| { |
| printf("CoverageTrace::GetName\n"); |
| return instance_name.c_str(); |
| } |
| |
| CADIReturn_t CoverageTrace::GetParameterInfos(uint32_t start_index, |
| uint32_t desired_num_of_params, |
| uint32_t* actual_num_of_params, |
| CADIParameterInfo_t* params) |
| { |
| |
| *actual_num_of_params = GetThePluginFactory()->GetNumberOfParameters(); |
| return GetThePluginFactory()->GetParameterInfos(params); |
| |
| } |
| |
| CADIReturn_t CoverageTrace::GetParameterInfo(const char* parameterName, |
| CADIParameterInfo_t* param) |
| { |
| uint32_t num_of_params = GetThePluginFactory()->GetNumberOfParameters(); |
| CADIParameterInfo_t* plugin_parameters = NULL; |
| |
| GetThePluginFactory()->GetParameterInfos(plugin_parameters); |
| |
| if (param == nullptr) |
| { |
| return CADI_STATUS_IllegalArgument; |
| } |
| |
| printf("Reading parameter info %s \n", parameterName); |
| for (uint32_t i = 0; i < num_of_params; ++i) |
| { |
| if (strcmp(plugin_parameters[i].name, parameterName) == 0) |
| { |
| *param = plugin_parameters[i]; |
| return CADI_STATUS_OK; |
| } |
| } |
| *param = plugin_parameters[0]; |
| return CADI_STATUS_OK; |
| |
| } |
| |
| CADIReturn_t CoverageTrace::GetParameterValues(uint32_t parameter_count, |
| uint32_t* actual_num_of_params_read, |
| CADIParameterValue_t *param_values_out) |
| { |
| if (param_values_out == nullptr || actual_num_of_params_read == nullptr) |
| { |
| return CADI_STATUS_IllegalArgument; |
| } |
| |
| *actual_num_of_params_read = 0; |
| for (uint32_t i = 0; i < parameter_count; ++i) |
| { |
| CADIParameterValue_t ¶m_value = param_values_out[i]; |
| |
| switch (param_value.parameterID) |
| { |
| case TRACE_FILE_PREFIX: |
| strncpy(param_value.stringValue, |
| this->trace_file_prefix.c_str(), |
| sizeof(param_value.stringValue)); |
| break; |
| |
| case TRACE_MODE: |
| param_value.intValue = this->mode_change->mode_mask; |
| break; |
| default: // unknown ID |
| |
| *actual_num_of_params_read = i; |
| return CADI_STATUS_IllegalArgument; |
| } |
| } |
| |
| *actual_num_of_params_read = parameter_count; |
| return CADI_STATUS_OK; |
| } |
| |
| CADIReturn_t CoverageTrace::SetParameterValues(uint32_t parameter_count, |
| CADIParameterValue_t* parameters, |
| CADIFactoryErrorMessage_t* error) |
| { |
| for (uint32_t i = 0; i < parameter_count; ++i) |
| { |
| CADIParameterValue_t ¶meter = parameters[i]; |
| switch(parameter.parameterID) |
| { |
| case TRACE_FILE_PREFIX: |
| this->trace_file_prefix = parameter.stringValue; |
| this->Save(); |
| break; |
| case TRACE_MODE: |
| this->mode_change->mode_mask = parameter.intValue; |
| break; |
| default: |
| ; |
| break; |
| } |
| } |
| return CADI_STATUS_OK; |
| } |
| |
| // Allows a client to obtain a reference to any of the interfaces that the |
| // component implements. The client specifies the id and revision of the |
| // interface that it wants to request. The component can return NULL if it |
| // doesn’t implement that interface, or only implements a lower revision. |
| // The client in this case is the Arm FVP model. |
| CAInterface *ThePluginFactory::ObtainInterface(if_name_t ifName, |
| if_rev_t minRev, |
| if_rev_t * actualRev) |
| { |
| printf("ThePluginFactory::ObtainInterface\n"); |
| // If someone is asking for the matching interface |
| if((strcmp(ifName,IFNAME()) == 0) && |
| // and the revision of this interface implementation is |
| (minRev <= IFREVISION())) |
| // at least what is being asked for |
| { |
| if (actualRev) // Make sure this is not a NULL pointer |
| *actualRev = IFREVISION(); |
| return static_cast<ThePluginFactory *>(this); |
| } |
| |
| if((strcmp(ifName, CAInterface::IFNAME()) == 0) && |
| minRev <= CAInterface::IFREVISION()) |
| { |
| if (actualRev) // Make sure this is not a NULL pointer |
| *actualRev = CAInterface::IFREVISION(); |
| return static_cast<CAInterface *>(this); |
| } |
| return NULL; |
| } |
| |
| uint32_t ThePluginFactory::GetNumberOfParameters() |
| { |
| printf("ThePluginFactory::GetNumberOfParameters\n"); |
| return TOTAL_PARAMETERS; |
| } |
| |
| eslapi::CADIReturn_t |
| ThePluginFactory::GetParameterInfos( |
| eslapi::CADIParameterInfo_t *parameter_info_list) |
| { |
| printf("ThePluginFactory::GetParameterInfos\n"); |
| parameter_info_list[0] = CADIParameterInfo_t( |
| TRACE_FILE_PREFIX, "trace-file-prefix", CADI_PARAM_STRING, |
| "Prefix of the trace files.", true, 0, 0, 0, "covtrace"); |
| parameter_info_list[1] = CADIParameterInfo_t( |
| TRACE_MODE, "trace-mode", CADI_PARAM_INT, |
| "Selects which modes to trace.", true, 0, 0xffffffff, 0xffffffff, 0); |
| return CADI_STATUS_OK; |
| } |
| |
| // Method that creates a new instance of the trace plugin |
| CAInterface *ThePluginFactory::Instantiate(const char *instance_name, |
| uint32_t param_nb, |
| eslapi::CADIParameterValue_t *values) |
| { |
| printf("ThePluginFactory::Instantiate\n"); |
| const char *trace_file_prefix = 0; |
| int mode_value = 0xffffffff; |
| |
| printf("CoverageTrace: number of params: %d\n", param_nb); |
| for (uint32_t i = 0; i < param_nb; ++i) { |
| if (values[i].parameterID == TRACE_FILE_PREFIX) { |
| trace_file_prefix = values[i].stringValue; |
| } else if (values[i].parameterID == TRACE_MODE) { |
| mode_value = values[i].intValue; |
| } else { |
| printf("\tCoverageTrace: got unexpected param %d\n", |
| values[i].parameterID); |
| } |
| } |
| return (PluginInstance*)new CoverageTrace(instance_name, trace_file_prefix, |
| mode_value); |
| } |
| |
| void ThePluginFactory::Release() |
| { |
| printf("ThePluginFactory::Release\n"); |
| } |
| |
| static ThePluginFactory factory_instance; |
| |
| // Entry point for the instantiation of the plugin. |
| // Returns a pointer to an static object to create the interface for the |
| // plugin. |
| CAInterface *GetCAInterface() |
| { |
| printf("********->GetCAInterface\n"); |
| return &factory_instance; |
| } |
| static ThePluginFactory *GetThePluginFactory() |
| { |
| return &factory_instance; |
| } |
| |
| // End of file CoverageTrace.cpp |