xref: /aosp_15_r20/external/OpenCSD/decoder/tests/source/mem_buff_demo.cpp (revision 02ca8ccacfba7e0df68f3332a95f3180334d6649)
1 /*
2 * \file     mem_buff_demo.cpp
3 * \brief    OpenCSD: using the library with memory buffers for data.
4 *
5 * \copyright  Copyright (c) 2018, ARM Limited. All Rights Reserved.
6 */
7 
8 /*
9 * Redistribution and use in source and binary forms, with or without modification,
10 * are permitted provided that the following conditions are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the copyright holder nor the names of its contributors
20 * may be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34 
35 /* Example showing techniques to drive library using only memory buffers as input data
36  * and image data, avoiding file i/o in main processing routines. (File I/O used to
37  * initially populate buffers but this can be replaced if data is generated by a client
38  * environment running live.)
39  */
40 
41 #include <cstdio>
42 #include <string>
43 #include <iostream>
44 #include <sstream>
45 #include <cstring>
46 
47 #include "opencsd.h"              // the library
48 
49 // uncomment below to use callback function for program memory image
50 // #define EXAMPLE_USE_MEM_CALLBACK
51 
52 /* Input trace buffer */
53 static uint8_t *input_trace_data = 0;
54 static uint32_t input_trace_data_size = 0;
55 
56 /* program memory image for decode */
57 static uint8_t *program_image_buffer = 0; // buffer for image data.
58 static uint32_t program_image_size = 0;   // size of program image data.
59 static ocsd_vaddr_t program_image_address = 0;  // load address on target of program image.
60 
61 /* a message logger to pass to the error logger / decoder. */
62 static ocsdMsgLogger logger;
63 
64 /* logger callback function - print out error strings */
65 class logCallback : public ocsdMsgLogStrOutI
66 {
67 public:
logCallback()68     logCallback() {};
~logCallback()69     virtual ~logCallback() {};
printOutStr(const std::string & outStr)70     virtual void printOutStr(const std::string &outStr)
71     {
72         std::cout << outStr.c_str();
73     }
74 };
75 static logCallback logCB;
76 
77 /* Decode tree is the main decoder framework - contains the frame unpacker,
78    packet and trace stream decoders, plus memory image references */
79 static DecodeTree *pDecoder = 0;
80 
81 /* an error logger - Decode tree registers all components with the error logger
82 so that errors can be correctly attributed and printed if required
83 */
84 static ocsdDefaultErrorLogger err_log;
85 
86 /* callbacks used by the library */
87 #ifdef EXAMPLE_USE_MEM_CALLBACK
88 // program memory image callback definition
89 uint32_t  mem_access_callback_fn(const void *p_context, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer);
90 #endif
91 
92 // callback for the decoder output elements
93 class DecoderOutputProcessor : public ITrcGenElemIn
94 {
95 public:
DecoderOutputProcessor()96     DecoderOutputProcessor() {};
~DecoderOutputProcessor()97     virtual ~DecoderOutputProcessor() {};
98 
TraceElemIn(const ocsd_trc_index_t index_sop,const uint8_t trc_chan_id,const OcsdTraceElement & elem)99     virtual ocsd_datapath_resp_t TraceElemIn(const ocsd_trc_index_t index_sop,
100         const uint8_t trc_chan_id,
101         const OcsdTraceElement &elem)
102     {
103         // must fully process or make a copy of data in here.
104         // element reference only valid for scope of call.
105 
106         // for the example program we will stringise and print -
107         // but this is a client program implmentation dependent.
108         std::string elemStr;
109         std::ostringstream oss;
110         oss << "Idx:" << index_sop << "; ID:" << std::hex << (uint32_t)trc_chan_id << "; ";
111         elem.toString(elemStr);
112         oss << elemStr << std::endl;
113         logger.LogMsg(oss.str());
114         return OCSD_RESP_CONT;
115     }
116 };
117 static DecoderOutputProcessor output;
118 
119 /* for test purposes we are initialising from files, but this could be generated test data as
120  part of a larger program and / or compiled in memory images.
121 
122  We have hardcoded in one of the snapshots supplied with the library
123  */
initDataBuffers()124 static int initDataBuffers()
125 {
126     FILE *fp;
127     std::string filename;
128     long size;
129     size_t bytes_read;
130 
131     /* the file names to create the data buffers */
132 #ifdef _WIN32
133     static const char *default_base_snapshot_path = "..\\..\\..\\snapshots";
134     static const char *juno_snapshot = "\\juno_r1_1\\";
135 #else
136     static const char *default_base_snapshot_path = "../../../snapshots";
137     static const char *juno_snapshot = "/juno_r1_1/";
138 #endif
139 
140     /* trace data and memory file dump names and values - taken from snapshot metadata */
141     static const char *trace_data_filename = "cstrace.bin";
142     static const char *memory_dump_filename = "kernel_dump.bin";
143     static ocsd_vaddr_t mem_dump_address = 0xFFFFFFC000081000;
144 
145     /* load up the trace data */
146     filename = default_base_snapshot_path;
147     filename += (std::string)juno_snapshot;
148     filename += (std::string)trace_data_filename;
149 
150     fp = fopen(filename.c_str(), "rb");
151     if (!fp)
152         return OCSD_ERR_FILE_ERROR;
153     fseek(fp, 0, SEEK_END);
154     size = ftell(fp);
155     input_trace_data_size = (uint32_t)size;
156     input_trace_data = new (std::nothrow) uint8_t[input_trace_data_size];
157     if (!input_trace_data) {
158         fclose(fp);
159         return OCSD_ERR_MEM;
160     }
161     rewind(fp);
162     bytes_read = fread(input_trace_data, 1, input_trace_data_size, fp);
163     fclose(fp);
164     if (bytes_read < (size_t)input_trace_data_size)
165         return OCSD_ERR_FILE_ERROR;
166 
167     /* load up a memory image */
168     filename = default_base_snapshot_path;
169     filename += (std::string)juno_snapshot;
170     filename += (std::string)memory_dump_filename;
171 
172     fp = fopen(filename.c_str(), "rb");
173     if (!fp)
174         return OCSD_ERR_FILE_ERROR;
175     fseek(fp, 0, SEEK_END);
176     size = ftell(fp);
177     program_image_size = (uint32_t)size;
178     program_image_buffer = new (std::nothrow) uint8_t[program_image_size];
179     if (!program_image_buffer) {
180         fclose(fp);
181         return OCSD_ERR_MEM;
182     }
183     rewind(fp);
184     bytes_read = fread(program_image_buffer, 1, program_image_size, fp);
185     fclose(fp);
186     if (bytes_read < (size_t)program_image_size)
187         return OCSD_ERR_FILE_ERROR;
188     program_image_address = mem_dump_address;
189     return OCSD_OK;
190 }
191 
createETMv4StreamDecoder()192 static ocsd_err_t createETMv4StreamDecoder()
193 {
194     ocsd_etmv4_cfg trace_config;
195     ocsd_err_t err = OCSD_OK;
196     EtmV4Config *pCfg = 0;
197 
198     /*
199     * populate the ETMv4 configuration structure with
200     * hard coded values from snapshot .ini files.
201     */
202 
203     trace_config.arch_ver = ARCH_V8;
204     trace_config.core_prof = profile_CortexA;
205 
206     trace_config.reg_configr = 0x000000C1;
207     trace_config.reg_traceidr = 0x00000010;   /* this is the trace ID -> 0x10, change this to analyse other streams in snapshot.*/
208     trace_config.reg_idr0 = 0x28000EA1;
209     trace_config.reg_idr1 = 0x4100F403;
210     trace_config.reg_idr2 = 0x00000488;
211     trace_config.reg_idr8 = 0x0;
212     trace_config.reg_idr9 = 0x0;
213     trace_config.reg_idr10 = 0x0;
214     trace_config.reg_idr11 = 0x0;
215     trace_config.reg_idr12 = 0x0;
216     trace_config.reg_idr13 = 0x0;
217 
218     pCfg = new (std::nothrow) EtmV4Config(&trace_config);
219     if (!pCfg)
220         return OCSD_ERR_MEM;
221 
222     err = pDecoder->createDecoder(OCSD_BUILTIN_DCD_ETMV4I,  /* etm v4 decoder */
223                                    OCSD_CREATE_FLG_FULL_DECODER, /* full trace decode */
224                                     pCfg);
225     delete pCfg;
226     return err;
227 }
228 
229 /* Create the decode tree and add the error logger,  stream decoder, memory image data to it.
230    Also register the output callback that processes the decoded trace packets. */
initialiseDecoder()231 static ocsd_err_t initialiseDecoder()
232 {
233     ocsd_err_t ret = OCSD_OK;
234 
235     /* use the creation function to get the type of decoder we want
236         either OCSD_TRC_SRC_SINGLE : single trace source - not frame formatted
237                OCSD_TRC_SRC_FRAME_FORMATTED :multi source - CoreSight trace frame
238         and set the config flags for operation
239                 OCSD_DFRMTR_FRAME_MEM_ALIGN: input data mem aligned -> no syncs
240 
241        For this test we create a decode that can unpack frames and is not expecting sync packets.
242      */
243     pDecoder = DecodeTree::CreateDecodeTree(OCSD_TRC_SRC_FRAME_FORMATTED, OCSD_DFRMTR_FRAME_MEM_ALIGN);
244     if (!pDecoder)
245         return OCSD_ERR_MEM;
246 
247     /* set up decoder logging - the message logger for output, and the error logger for the library */
248     logger.setLogOpts(ocsdMsgLogger::OUT_STR_CB); /* no IO from the logger, just a string callback. */
249     logger.setStrOutFn(&logCB); /* set the callback - in this example it will go to stdio  but this is up to the implementor. */
250 
251     // for debugging - stdio and file
252 //    logger.setLogOpts(ocsdMsgLogger::OUT_FILE | ocsdMsgLogger::OUT_STDOUT);
253 
254     err_log.initErrorLogger(OCSD_ERR_SEV_INFO);
255     err_log.setOutputLogger(&logger); /* pass the output logger to the error logger. */
256 
257     pDecoder->setAlternateErrorLogger(&err_log); /* pass the error logger to the decoder, do not use the library version. */
258 
259     /* now set up the elements that the decoder needs */
260 
261     /* we will decode one of the streams in this example
262        create a Full decode ETMv4 stream decoder */
263     ret = createETMv4StreamDecoder();
264     if (ret != OCSD_OK)
265         return ret;
266 
267     /* as this has full decode we must supply a memory image. */
268 
269     ret = pDecoder->createMemAccMapper();   // the mapper is needed to add code images to.
270     if (ret != OCSD_OK)
271         return ret;
272 
273 #ifdef EXAMPLE_USE_MEM_CALLBACK
274     // in this example we have a single buffer so we demonstrate how to use a callback.
275     // we are passing the buffer pointer as context as we only have one buffer, but this
276     // could be a structure that is a list of memory image buffers. Context is entirely
277     // client defined.
278     // Always use OCSD_MEM_SPACE_ANY unless there is a reason to restrict the image to a specific
279     // memory space.
280     pDecoder->addCallbackMemAcc(program_image_address, program_image_address + program_image_size-1,
281         OCSD_MEM_SPACE_ANY,mem_access_callback_fn, program_image_buffer);
282 #else
283     // or we can use the built in memory buffer interface - split our one buffer into two to
284     // demonstrate the addition of multiple regions
285     ocsd_vaddr_t block1_st, block2_st;
286     uint32_t block1_sz, block2_sz;
287     uint8_t *p_block1, *p_block2;
288 
289     // break our single buffer into 2 buffers for demo purposes
290     block1_sz = program_image_size / 2;
291     block1_sz &= ~0x3; // align
292     block2_sz = program_image_size - block1_sz;
293     block1_st = program_image_address;  // loaded program memory start address of program
294     block2_st = program_image_address + block1_sz;
295     p_block1 = program_image_buffer;
296     p_block2 = program_image_buffer + block1_sz;
297 
298     /* how to add 2 "separate" buffers to the decoder */
299     // Always use OCSD_MEM_SPACE_ANY unless there is a reason to restrict the image to a specific
300     // memory space.
301     ret = pDecoder->addBufferMemAcc(block1_st, OCSD_MEM_SPACE_ANY, p_block1, block1_sz);
302     if (ret != OCSD_OK)
303         return ret;
304 
305     ret = pDecoder->addBufferMemAcc(block2_st, OCSD_MEM_SPACE_ANY, p_block2, block2_sz);
306     if (ret != OCSD_OK)
307         return ret;
308 #endif
309 
310     /* finally we need to provide an output callback to recieve the decoded information */
311     pDecoder->setGenTraceElemOutI(&output);
312     return ret;
313 }
314 
315 /* get rid of the objects we created */
destroyDecoder()316 static void destroyDecoder()
317 {
318     delete pDecoder;
319     delete [] input_trace_data;
320     delete [] program_image_buffer;
321 }
322 
323 #ifdef EXAMPLE_USE_MEM_CALLBACK
324 /* if we set up to use a callback to access memory image then this is what will be called. */
325 /* In this case the client must do all the work in determining if the requested address is in the
326    memory area. */
mem_access_callback_fn(const void * p_context,const ocsd_vaddr_t address,const ocsd_mem_space_acc_t mem_space,const uint32_t reqBytes,uint8_t * byteBuffer)327 uint32_t  mem_access_callback_fn(const void *p_context, const ocsd_vaddr_t address,
328     const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer)
329 {
330     ocsd_vaddr_t buf_end_address = program_image_address + program_image_size - 1;
331     uint32_t read_bytes = reqBytes;
332 
333     /* context should be our memory image buffer - if not return 0 bytes read */
334     if (p_context != program_image_buffer)
335         return 0;
336 
337     /* not concerned with memory spaces - assume all global */
338     if ((address < program_image_address) || (address > buf_end_address))
339         return 0;   // requested address not in our buffer.
340 
341     // if requested bytes from address more than we have, only read to end of buffer
342     if ((address + reqBytes - 1) > buf_end_address)
343         read_bytes = (uint32_t)(buf_end_address - (address - 1));
344 
345     // copy the requested data.
346     memcpy(byteBuffer, program_image_buffer + (address - program_image_address), read_bytes);
347 
348     return read_bytes;
349 }
350 #endif
351 
352 /* use the decoder to process the global trace data buffer */
processTraceData(uint32_t * bytes_done)353 static ocsd_datapath_resp_t processTraceData(uint32_t *bytes_done)
354 {
355     /* process in blocks of fixed size. */
356     #define DATA_CHUNK_SIZE 2048
357 
358     ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
359     uint32_t block_size, buff_offset, bytes_to_do = input_trace_data_size, bytes_processed;
360     ocsd_trc_index_t index = 0;
361 
362     /* process the data in chunks, until either all done or
363     * error occurs.
364     */
365     while ((resp == OCSD_RESP_CONT) && (bytes_to_do))
366     {
367         /* size up a block of input data */
368         block_size = (bytes_to_do >= DATA_CHUNK_SIZE) ? DATA_CHUNK_SIZE : bytes_to_do;
369         buff_offset = input_trace_data_size - bytes_to_do;
370 
371         /* push it through the decoder */
372         resp = pDecoder->TraceDataIn(OCSD_OP_DATA, index, block_size,
373             input_trace_data + buff_offset, &bytes_processed);
374 
375         /* adjust counter per bytes processed */
376         bytes_to_do -= bytes_processed;
377         index += bytes_processed;
378     }
379 
380     /* if all done then signal end of trace - flushes out any remaining data */
381     if (!bytes_to_do)
382         resp = pDecoder->TraceDataIn(OCSD_OP_EOT, 0, 0, 0, 0);
383 
384     /* return amount processed */
385     *bytes_done = input_trace_data_size - bytes_to_do;
386     return resp;
387 }
388 
389 /* main routine - init input data, decode, finish ... */
main(int argc,char * argv[])390 int main(int argc, char* argv[])
391 {
392     int ret = OCSD_OK;
393     ocsd_datapath_resp_t retd;
394     char msg[256];
395     uint32_t bytes_done;
396 
397     /* initialise all the data needed for decode */
398     if ((ret = initDataBuffers()) != OCSD_OK)
399     {
400         logger.LogMsg("Failed to create trace data buffers\n");
401         return ret;
402     }
403     /* initialise a decoder object */
404     if ((ret = initialiseDecoder()) == OCSD_OK)
405     {
406             retd = processTraceData(&bytes_done);
407             if (!OCSD_DATA_RESP_IS_CONT(retd))
408             {
409                 ret = OCSD_ERR_DATA_DECODE_FATAL;
410                 logger.LogMsg("Processing failed with data error\n");
411             }
412 
413         /* get rid of the decoder and print a brief result. */
414         destroyDecoder();
415         sprintf(msg, "Processed %u bytes out of %u\n", bytes_done, input_trace_data_size);
416         logger.LogMsg(msg);
417     }
418     else
419         logger.LogMsg("Failed to create decoder for trace processing\n");
420     return ret;
421 }
422