• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /* Input trace buffer */
50 static uint8_t *input_trace_data = 0;
51 static uint32_t input_trace_data_size = 0;
52 
53 /* program memory image for decode */
54 static uint8_t *program_image_buffer = 0; // buffer for image data.
55 static uint32_t program_image_size = 0;   // size of program image data.
56 static ocsd_vaddr_t program_image_address = 0;  // load address on target of program image.
57 
58 /* a message logger to pass to the error logger / decoder. */
59 static ocsdMsgLogger logger;
60 
61 static bool use_callback = false;
62 static bool use_logfile = false;
63 static bool no_print = false;
64 
65     /* the file names to create the data buffers */
66 #ifdef _WIN32
67 static const char *default_base_snapshot_path = "..\\..\\..\\snapshots";
68 static const char *juno_snapshot = "\\juno_r1_1\\";
69 #else
70 static const char *default_base_snapshot_path = "../../../snapshots";
71 static const char *juno_snapshot = "/juno_r1_1/";
72 #endif
73 
74 static const char *usr_snapshot_path = 0;
75 #define MAX_TRACE_FILE_PATH_LEN 512
76 
77 /* logger callback class - print out error strings.
78  * Example of custom log printer callback
79  */
80 class logCallback : public ocsdMsgLogStrOutI
81 {
82 public:
logCallback()83     logCallback()
84     {
85         out_console = true;
86         out_file = false;
87     }
88 
~logCallback()89     virtual ~logCallback()
90     {
91         closeOutFile();
92     }
93 
printOutStr(const std::string & outStr)94     virtual void printOutStr(const std::string& outStr)
95     {
96         if (out_console)
97             std::cout << outStr.c_str();
98         printOutFile(outStr);
99     }
100 
setOutConsole(const bool console_output)101     void setOutConsole(const bool console_output)
102     {
103         out_console = console_output;
104     }
105 
setOutFile(const std::string & filename)106     void setOutFile(const std::string &filename)
107     {
108         openOutFile(filename);
109     }
110 
111 private:
openOutFile(const std::string & filename)112     void openOutFile(const std::string &filename)
113     {
114 
115         m_out_file.open(filename.c_str(), std::fstream::out | std::fstream::app);
116         if (m_out_file.is_open())
117             out_file = true;
118     }
119 
printOutFile(const std::string & str)120     void printOutFile(const std::string& str)
121     {
122         if (out_file)
123         {
124             m_out_file << str;
125         }
126     }
127 
closeOutFile()128     void closeOutFile()
129     {
130         if (out_file) {
131             // close output file
132             m_out_file.close();
133         }
134     }
135     bool out_file;
136     bool out_console;
137     std::fstream m_out_file;
138 };
139 static logCallback logCB;
140 
141 /* Decode tree is the main decoder framework - contains the frame unpacker,
142    packet and trace stream decoders, plus memory image references */
143 static DecodeTree *pDecoder = 0;
144 
145 /* an error logger - Decode tree registers all components with the error logger
146 so that errors can be correctly attributed and printed if required
147 */
148 static ocsdDefaultErrorLogger err_log;
149 
150 /* callbacks used by the library */
151 
152 // program memory image callback definition
153 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);
154 
155 // callback for the decoder output elements
156 class DecoderOutputProcessor : public ITrcGenElemIn
157 {
158 public:
DecoderOutputProcessor()159     DecoderOutputProcessor() {};
~DecoderOutputProcessor()160     virtual ~DecoderOutputProcessor() {};
161 
TraceElemIn(const ocsd_trc_index_t index_sop,const uint8_t trc_chan_id,const OcsdTraceElement & elem)162     virtual ocsd_datapath_resp_t TraceElemIn(const ocsd_trc_index_t index_sop,
163         const uint8_t trc_chan_id,
164         const OcsdTraceElement &elem)
165     {
166         // must fully process or make a copy of data in here.
167         // element reference only valid for scope of call.
168 
169         // for the example program we will stringise and print -
170         // but this is a client program implmentation dependent.
171         std::string elemStr;
172         std::ostringstream oss;
173         oss << "Idx:" << index_sop << "; ID:" << std::hex << (uint32_t)trc_chan_id << "; ";
174         elem.toString(elemStr);
175         oss << elemStr << std::endl;
176         logger.LogMsg(oss.str());
177         return OCSD_RESP_CONT;
178     }
179 };
180 static DecoderOutputProcessor output;
181 
182 /* for test purposes we are initialising from files, but this could be generated test data as
183  part of a larger program and / or compiled in memory images.
184 
185  We have hardcoded in one of the snapshots supplied with the library
186  */
initDataBuffers()187 static int initDataBuffers()
188 {
189     FILE *fp;
190     std::string filename;
191     long size;
192     size_t bytes_read;
193 
194     /* trace data and memory file dump names and values - taken from snapshot metadata */
195     static const char *trace_data_filename = "cstrace.bin";
196     static const char *memory_dump_filename = "kernel_dump.bin";
197     static ocsd_vaddr_t mem_dump_address = 0xFFFFFFC000081000;
198 
199     /* load up the trace data */
200     filename = (usr_snapshot_path ? usr_snapshot_path : default_base_snapshot_path);
201     filename += (std::string)juno_snapshot;
202     filename += (std::string)trace_data_filename;
203 
204     fp = fopen(filename.c_str(), "rb");
205     if (!fp)
206         return OCSD_ERR_FILE_ERROR;
207     fseek(fp, 0, SEEK_END);
208     size = ftell(fp);
209     input_trace_data_size = (uint32_t)size;
210     input_trace_data = new (std::nothrow) uint8_t[input_trace_data_size];
211     if (!input_trace_data) {
212         fclose(fp);
213         return OCSD_ERR_MEM;
214     }
215     rewind(fp);
216     bytes_read = fread(input_trace_data, 1, input_trace_data_size, fp);
217     fclose(fp);
218     if (bytes_read < (size_t)input_trace_data_size)
219         return OCSD_ERR_FILE_ERROR;
220 
221     /* load up a memory image */
222     filename = (usr_snapshot_path ? usr_snapshot_path : default_base_snapshot_path);
223     filename += (std::string)juno_snapshot;
224     filename += (std::string)memory_dump_filename;
225 
226     fp = fopen(filename.c_str(), "rb");
227     if (!fp)
228         return OCSD_ERR_FILE_ERROR;
229     fseek(fp, 0, SEEK_END);
230     size = ftell(fp);
231     program_image_size = (uint32_t)size;
232     program_image_buffer = new (std::nothrow) uint8_t[program_image_size];
233     if (!program_image_buffer) {
234         fclose(fp);
235         return OCSD_ERR_MEM;
236     }
237     rewind(fp);
238     bytes_read = fread(program_image_buffer, 1, program_image_size, fp);
239     fclose(fp);
240     if (bytes_read < (size_t)program_image_size)
241         return OCSD_ERR_FILE_ERROR;
242     program_image_address = mem_dump_address;
243     return OCSD_OK;
244 }
245 
createETMv4StreamDecoder()246 static ocsd_err_t createETMv4StreamDecoder()
247 {
248     ocsd_etmv4_cfg trace_config;
249     ocsd_err_t err = OCSD_OK;
250     EtmV4Config *pCfg = 0;
251 
252     /*
253     * populate the ETMv4 configuration structure with
254     * hard coded values from snapshot .ini files.
255     */
256 
257     trace_config.arch_ver = ARCH_V8;
258     trace_config.core_prof = profile_CortexA;
259 
260     trace_config.reg_configr = 0x000000C1;
261     trace_config.reg_traceidr = 0x00000010;   /* this is the trace ID -> 0x10, change this to analyse other streams in snapshot.*/
262     trace_config.reg_idr0 = 0x28000EA1;
263     trace_config.reg_idr1 = 0x4100F403;
264     trace_config.reg_idr2 = 0x00000488;
265     trace_config.reg_idr8 = 0x0;
266     trace_config.reg_idr9 = 0x0;
267     trace_config.reg_idr10 = 0x0;
268     trace_config.reg_idr11 = 0x0;
269     trace_config.reg_idr12 = 0x0;
270     trace_config.reg_idr13 = 0x0;
271 
272     pCfg = new (std::nothrow) EtmV4Config(&trace_config);
273     if (!pCfg)
274         return OCSD_ERR_MEM;
275 
276     err = pDecoder->createDecoder(OCSD_BUILTIN_DCD_ETMV4I,  /* etm v4 decoder */
277                                    OCSD_CREATE_FLG_FULL_DECODER, /* full trace decode */
278                                     pCfg);
279     delete pCfg;
280     return err;
281 }
282 
283 /* Create the decode tree and add the error logger,  stream decoder, memory image data to it.
284    Also register the output callback that processes the decoded trace packets. */
initialiseDecoder()285 static ocsd_err_t initialiseDecoder()
286 {
287     ocsd_err_t ret = OCSD_OK;
288 
289     /* use the creation function to get the type of decoder we want
290         either OCSD_TRC_SRC_SINGLE : single trace source - not frame formatted
291                OCSD_TRC_SRC_FRAME_FORMATTED :multi source - CoreSight trace frame
292         and set the config flags for operation
293                 OCSD_DFRMTR_FRAME_MEM_ALIGN: input data mem aligned -> no syncs
294 
295        For this test we create a decode that can unpack frames and is not expecting sync packets.
296      */
297     pDecoder = DecodeTree::CreateDecodeTree(OCSD_TRC_SRC_FRAME_FORMATTED, OCSD_DFRMTR_FRAME_MEM_ALIGN);
298     if (!pDecoder)
299         return OCSD_ERR_MEM;
300 
301     /* set up decoder logging - the message logger for output, and the error logger for the library */
302     logger.setLogOpts(ocsdMsgLogger::OUT_STR_CB); /* no IO from the logger, just a string callback. */
303     logger.setStrOutFn(&logCB); /* set the callback - in this example it will go to stdio  but this is up to the implementor. */
304 
305     // for debugging - stdio and file
306 //    logger.setLogOpts(ocsdMsgLogger::OUT_FILE | ocsdMsgLogger::OUT_STDOUT);
307 
308     err_log.initErrorLogger(OCSD_ERR_SEV_INFO);
309     err_log.setOutputLogger(&logger); /* pass the output logger to the error logger. */
310 
311     pDecoder->setAlternateErrorLogger(&err_log); /* pass the error logger to the decoder, do not use the library version. */
312 
313     /* now set up the elements that the decoder needs */
314 
315     /* we will decode one of the streams in this example
316        create a Full decode ETMv4 stream decoder */
317     ret = createETMv4StreamDecoder();
318     if (ret != OCSD_OK)
319         return ret;
320 
321     /* as this has full decode we must supply a memory image. */
322 
323     ret = pDecoder->createMemAccMapper();   // the mapper is needed to add code images to.
324     if (ret != OCSD_OK)
325         return ret;
326 
327     if (use_callback)
328     {
329         // in this example we have a single buffer so we demonstrate how to use a callback.
330         // we are passing the buffer pointer as context as we only have one buffer, but this
331         // could be a structure that is a list of memory image buffers. Context is entirely
332         // client defined.
333         // Always use OCSD_MEM_SPACE_ANY unless there is a reason to restrict the image to a specific
334         // memory space.
335         pDecoder->addCallbackMemAcc(program_image_address, program_image_address + program_image_size - 1,
336             OCSD_MEM_SPACE_ANY, mem_access_callback_fn, program_image_buffer);
337     }
338     else
339     {
340         // or we can use the built in memory buffer interface - split our one buffer into two to
341         // demonstrate the addition of multiple regions
342         ocsd_vaddr_t block1_st, block2_st;
343         uint32_t block1_sz, block2_sz;
344         uint8_t* p_block1, * p_block2;
345 
346         // break our single buffer into 2 buffers for demo purposes
347         block1_sz = program_image_size / 2;
348         block1_sz &= ~0x3; // align
349         block2_sz = program_image_size - block1_sz;
350         block1_st = program_image_address;  // loaded program memory start address of program
351         block2_st = program_image_address + block1_sz;
352         p_block1 = program_image_buffer;
353         p_block2 = program_image_buffer + block1_sz;
354 
355         /* how to add 2 "separate" buffers to the decoder */
356         // Always use OCSD_MEM_SPACE_ANY unless there is a reason to restrict the image to a specific
357         // memory space.
358         ret = pDecoder->addBufferMemAcc(block1_st, OCSD_MEM_SPACE_ANY, p_block1, block1_sz);
359         if (ret != OCSD_OK)
360             return ret;
361 
362         ret = pDecoder->addBufferMemAcc(block2_st, OCSD_MEM_SPACE_ANY, p_block2, block2_sz);
363         if (ret != OCSD_OK)
364             return ret;
365     }
366 
367     /* finally we need to provide an output callback to recieve the decoded information */
368     pDecoder->setGenTraceElemOutI(&output);
369     return ret;
370 }
371 
372 /* get rid of the objects we created */
destroyDecoder()373 static void destroyDecoder()
374 {
375     delete pDecoder;
376     delete [] input_trace_data;
377     delete [] program_image_buffer;
378 }
379 
380 
381 /* if we set up to use a callback to access memory image then this is what will be called. */
382 /* In this case the client must do all the work in determining if the requested address is in the
383    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)384 uint32_t  mem_access_callback_fn(const void *p_context, const ocsd_vaddr_t address,
385     const ocsd_mem_space_acc_t mem_space, const uint32_t reqBytes, uint8_t *byteBuffer)
386 {
387     ocsd_vaddr_t buf_end_address = program_image_address + program_image_size - 1;
388     uint32_t read_bytes = reqBytes;
389 
390     /* context should be our memory image buffer - if not return 0 bytes read */
391     if (p_context != program_image_buffer)
392         return 0;
393 
394     /* not concerned with memory spaces - assume all global */
395     if ((address < program_image_address) || (address > buf_end_address))
396         return 0;   // requested address not in our buffer.
397 
398     // if requested bytes from address more than we have, only read to end of buffer
399     if ((address + reqBytes - 1) > buf_end_address)
400         read_bytes = (uint32_t)(buf_end_address - (address - 1));
401 
402     // copy the requested data.
403     memcpy(byteBuffer, program_image_buffer + (address - program_image_address), read_bytes);
404 
405     return read_bytes;
406 }
407 
408 /* use the decoder to process the global trace data buffer */
processTraceData(uint32_t * bytes_done)409 static ocsd_datapath_resp_t processTraceData(uint32_t *bytes_done)
410 {
411     /* process in blocks of fixed size. */
412     #define DATA_CHUNK_SIZE 2048
413 
414     ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
415     uint32_t block_size, buff_offset, bytes_to_do = input_trace_data_size, bytes_processed;
416     ocsd_trc_index_t index = 0;
417 
418     /* process the data in chunks, until either all done or
419     * error occurs.
420     */
421     while ((resp == OCSD_RESP_CONT) && (bytes_to_do))
422     {
423         /* size up a block of input data */
424         block_size = (bytes_to_do >= DATA_CHUNK_SIZE) ? DATA_CHUNK_SIZE : bytes_to_do;
425         buff_offset = input_trace_data_size - bytes_to_do;
426 
427         /* push it through the decoder */
428         resp = pDecoder->TraceDataIn(OCSD_OP_DATA, index, block_size,
429             input_trace_data + buff_offset, &bytes_processed);
430 
431         /* adjust counter per bytes processed */
432         bytes_to_do -= bytes_processed;
433         index += bytes_processed;
434     }
435 
436     /* if all done then signal end of trace - flushes out any remaining data */
437     if (!bytes_to_do)
438         resp = pDecoder->TraceDataIn(OCSD_OP_EOT, 0, 0, 0, 0);
439 
440     /* return amount processed */
441     *bytes_done = input_trace_data_size - bytes_to_do;
442     return resp;
443 }
444 
printhelp()445 void printhelp()
446 {
447     printf("mem_buff_demo memory accessor test program\n");
448     printf("==========================================\n\n");
449     printf("options:-\n");
450     printf("    -callback       : test callback mem acc functionality\n");
451     printf("    -logfile        : output test result to logfile\n");
452     printf("    -noprint        : no output to console\n");
453     printf("    -ss_path <path> : set path to snapshot directory\n");
454     printf("\n\n========================================\n\n");
455 
456 }
457 
process_cmd_line_opts(int argc,char * argv[])458 int process_cmd_line_opts(int argc, char* argv[])
459 {
460     int options_to_process = argc - 1;
461     int opt_idx = 1;
462     std::string opt;
463 
464     while(options_to_process)
465     {
466         opt = argv[opt_idx];
467 
468         if (opt == "-callback")
469             use_callback = true;
470         else if (opt == "-logfile")
471             use_logfile = true;
472         else if (opt == "-noprint")
473             no_print = true;
474         else if (opt == "-ss_path")
475         {
476             opt_idx++;
477             options_to_process--;
478 
479             if ((opt_idx >= argc) || (strlen(argv[opt_idx]) == 0))
480             {
481                 printf("-ss_path: Missing path parameter or zero length\n");
482                 return -1;
483             }
484             else
485             {
486                 if (strlen(argv[opt_idx]) > (MAX_TRACE_FILE_PATH_LEN - 32))
487                 {
488                     printf("-ss_path: path too long\n");
489                     return -1;
490                 }
491                 usr_snapshot_path = argv[opt_idx];
492             }
493         }
494         else
495         {
496             printhelp();
497             return -1;
498         }
499         opt_idx++;
500         options_to_process--;
501     }
502     return 0;
503 }
504 
log_cmd_line_opts(int argc,char * argv[])505 void log_cmd_line_opts(int argc, char* argv[])
506 {
507     std::ostringstream oss;
508     oss << "Test Command Line:-\n";
509     oss << argv[0] << "   ";
510     for (int i = 1; i < argc; i++)
511     {
512         oss << argv[i] << "  ";
513     }
514     oss << "\n\n";
515     logCB.printOutStr(oss.str());
516 }
517 
518 /* main routine - init input data, decode, finish ... */
main(int argc,char * argv[])519 int main(int argc, char* argv[])
520 {
521     int ret = OCSD_OK;
522     ocsd_datapath_resp_t retd;
523     char msg[256];
524     uint32_t bytes_done;
525 
526     if (process_cmd_line_opts(argc, argv) != 0)
527         return -1;
528 
529     if (use_logfile)
530         logCB.setOutFile(use_callback ? "mem_buff_demo_cb.ppl" : "mem_buff_demo.ppl");
531     if (no_print)
532         logCB.setOutConsole(false);
533 
534     logCB.printOutStr("MemBuffDemo\n--------------\n\n");
535     log_cmd_line_opts(argc, argv);
536 
537 
538     /* initialise all the data needed for decode */
539     if ((ret = initDataBuffers()) != OCSD_OK)
540     {
541         logger.LogMsg("Failed to create trace data buffers\n");
542         return ret;
543     }
544     /* initialise a decoder object */
545     if ((ret = initialiseDecoder()) == OCSD_OK)
546     {
547             retd = processTraceData(&bytes_done);
548             if (!OCSD_DATA_RESP_IS_CONT(retd))
549             {
550                 ret = OCSD_ERR_DATA_DECODE_FATAL;
551                 logger.LogMsg("Processing failed with data error\n");
552             }
553 
554         /* get rid of the decoder and print a brief result. */
555         destroyDecoder();
556         sprintf(msg, "Processed %u bytes out of %u\n", bytes_done, input_trace_data_size);
557         logger.LogMsg(msg);
558     }
559     else
560         logger.LogMsg("Failed to create decoder for trace processing\n");
561     return ret;
562 }
563