summaryrefslogtreecommitdiffstats
path: root/src/tools/trace/ppe2fsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/trace/ppe2fsp.c')
-rwxr-xr-xsrc/tools/trace/ppe2fsp.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/src/tools/trace/ppe2fsp.c b/src/tools/trace/ppe2fsp.c
new file mode 100755
index 00000000..c1ddc610
--- /dev/null
+++ b/src/tools/trace/ppe2fsp.c
@@ -0,0 +1,532 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/tools/trace/ppe2fsp.c $ */
+/* */
+/* OpenPOWER sbe Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2015,2016 */
+/* */
+/* */
+/* 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. */
+/* */
+/* IBM_PROLOG_END_TAG */
+#include "pk_trace.h"
+#include "ppe2fsp.h"
+#include "trac_interface.h"
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdint.h>
+
+#define TRACE_BUF_VERSION 0x01 /*!< Trace buffer version */
+#define TRACE_FIELDTRACE 0x4654 /*!< Field Trace - "FT" */
+#define TRACE_FIELDBIN 0x4644 /*!< Binary Field Trace - "FD" */
+
+#define TRAC_TIME_REAL 0 // upper 32 = seconds, lower 32 = nanoseconds
+#define TRAC_TIME_50MHZ 1
+#define TRAC_TIME_200MHZ 2
+#define TRAC_TIME_167MHZ 3 // 166666667Hz
+
+typedef struct
+{
+ trace_entry_stamp_t stamp;
+ trace_entry_head_t head;
+ union
+ {
+ uint8_t data[PK_TRACE_MAX_BINARY + 1]; //add 1 byte for padding
+ uint32_t parms[PK_TRACE_MAX_PARMS];
+ };
+ uint32_t size;
+}largest_fsp_entry_t;
+
+typedef struct
+{
+ union
+ {
+ uint8_t binary_data[PK_TRACE_MAX_BINARY + 1];
+ struct
+ {
+ uint8_t rsvd[(PK_TRACE_MAX_BINARY + 1) - (PK_TRACE_MAX_PARMS * sizeof(uint32_t))];
+ uint32_t parms[PK_TRACE_MAX_PARMS];
+ };
+ };
+ PkTraceEntryFooter footer;
+}LargestPpeEntry;
+
+//convert a ppe timestamp to an fsp trace timestamp
+uint64_t ppe2fsp_time(uint64_t ppe_time, uint32_t hz)
+{
+ uint32_t seconds;
+ uint32_t remainder;
+ uint32_t nseconds;
+
+ //convert from ppe ticks to seconds and nanoseconds
+ seconds = ppe_time / hz;
+ remainder = ppe_time - (((uint64_t)seconds) * hz);
+ nseconds = (((uint64_t)remainder) * 1000000000) / hz;
+ return (((uint64_t)seconds) << 32) | nseconds;
+}
+
+//Writes an fsp trace entry to the fsp trace buffer
+void fsp_put_entry(trace_buf_head_t* tb, largest_fsp_entry_t* fte, size_t entry_size, uint32_t bytes_left)
+{
+ char* buffer = ((char*)tb) + sizeof(trace_buf_head_t);
+ char* tb_start;
+ char* fte_start;
+ uint32_t copy_bytes;
+
+ if(entry_size <= bytes_left)
+ {
+ tb_start = buffer + bytes_left - entry_size;
+ fte_start = (char*)fte;
+ copy_bytes = entry_size;
+ }
+ else
+ {
+ tb_start = buffer;
+ fte_start = ((char*)fte) + (entry_size - bytes_left);
+ copy_bytes = bytes_left;
+ }
+
+ memcpy(tb_start, fte_start, copy_bytes);
+}
+
+
+//convert a ppe trace entry to an fsp trace entry
+size_t pte2fte(PkTraceBuffer* ptb,
+ LargestPpeEntry* pte,
+ size_t pte_size,
+ largest_fsp_entry_t* fte,
+ uint64_t ppe_time64)
+{
+ size_t entry_size;
+ PkTraceGeneric* pte_footer = &pte->footer.generic;
+ uint32_t format;
+ uint32_t hash32;
+ uint32_t hash32_partial;
+ uint32_t* parm_start;
+ uint32_t parm_bytes;
+ uint64_t fsp_time64;
+
+ //convert the ppe trace time to an fsp trace time
+ fsp_time64 = ppe2fsp_time(ppe_time64, ntohl(ptb->hz));
+
+ //fill in the 64 bit timestamp
+ fte->stamp.tbh = htonl((uint32_t)(fsp_time64 >> 32));
+ fte->stamp.tbl = htonl((uint32_t)(fsp_time64 & 0x00000000ffffffffull));
+
+ //use the ppe instance id as the thread id.
+ fte->stamp.tid = htonl((uint32_t)ntohs(ptb->instance_id));
+
+ //merge the hash prefix and the string_id fields together for a 32 bit hash value
+ hash32 = ((uint32_t)ntohs(ptb->hash_prefix)) << 16;
+ hash32 |= pte_footer->string_id;
+ fte->head.hash = htonl(hash32);
+
+ //generate the 32bit hash value for a partial trace entry in case it's needed
+ hash32_partial = ((uint32_t)ntohs(ptb->hash_prefix)) << 16;
+ hash32_partial |= ntohs(ptb->partial_trace_hash);
+
+ //set the line number to 1
+ fte->head.line = htonl(1);
+
+ //determine the FSP trace format
+ format = PK_GET_TRACE_FORMAT(pte_footer->time_format.word32);
+ if(format == PK_TRACE_FORMAT_BINARY)
+ {
+ fte->head.tag = htons(TRACE_FIELDBIN);
+ }
+ else
+ {
+ fte->head.tag = htons(TRACE_FIELDTRACE);
+ }
+
+ parm_start = (uint32_t*)(((char*)pte) + (sizeof(LargestPpeEntry) - pte_size));
+
+ //fill in the parameters/binary data and size at the end
+ switch(format)
+ {
+
+ case PK_TRACE_FORMAT_TINY:
+ //one or 0 parameters
+ entry_size = sizeof(trace_entry_stamp_t) +
+ sizeof(trace_entry_head_t) +
+ sizeof(uint32_t);
+ fte->parms[0] = htonl((uint32_t)(pte_footer->parm16));
+ fte->head.length = htons(sizeof(uint32_t));
+ parm_bytes = 0;
+ break;
+
+ case PK_TRACE_FORMAT_BIG:
+ //1 - 4 parameters
+ //
+ //If the trace entry data is incomplete (not all parm data
+ //had been written at the time the trace was captured) then
+ //we will write a trace to the fsp buffer that says
+ //"PARTIAL TRACE ENTRY. HASH_ID = %d"
+ if(pte_footer->complete)
+ {
+ parm_bytes = pte_footer->bytes_or_parms_count * sizeof(uint32_t);
+ fte->head.length = htons(parm_bytes + sizeof(uint32_t));
+ entry_size = sizeof(trace_entry_stamp_t) +
+ sizeof(trace_entry_head_t) +
+ parm_bytes + sizeof(uint32_t);
+ }
+ else
+ {
+ parm_bytes = 0;
+ entry_size = sizeof(trace_entry_stamp_t) +
+ sizeof(trace_entry_head_t) +
+ sizeof(uint32_t);
+ fte->parms[0] = fte->head.hash; //already corrected for endianess
+ fte->head.hash = htonl(hash32_partial);
+ fte->head.length = htons(sizeof(uint32_t));
+ }
+ break;
+
+ case PK_TRACE_FORMAT_BINARY:
+ //If the trace entry data is incomplete (not all parm data
+ //had been written at the time the trace was captured) then
+ //we will write a trace to the fsp buffer that says
+ //"PARTIAL TRACE ENTRY. HASH_ID = %d"
+ if(pte_footer->complete)
+ {
+ parm_bytes = pte_footer->bytes_or_parms_count;
+ fte->head.length = htons((uint16_t)parm_bytes);
+ entry_size = sizeof(trace_entry_stamp_t) +
+ sizeof(trace_entry_head_t) +
+ parm_bytes;
+
+ //pad to 4 byte boundary
+ entry_size = (entry_size + 3) & ~3;
+ }
+ else
+ {
+ parm_bytes = 0;
+ entry_size = sizeof(trace_entry_stamp_t) +
+ sizeof(trace_entry_head_t) +
+ sizeof(uint32_t);
+ fte->parms[0] = fte->head.hash;
+ fte->head.hash = htonl(hash32_partial);
+ fte->head.length = htons(sizeof(uint32_t));
+ fte->head.tag = htons(TRACE_FIELDTRACE);
+ }
+ break;
+
+
+ default:
+ entry_size = 0;
+ parm_bytes = 0;
+ break;
+ }
+
+ //copy parameter bytes to the fsp entry if necessary
+ if(parm_bytes)
+ {
+ memcpy(fte->data, parm_start, parm_bytes);
+ }
+
+ //add the entry size to the end
+ if(entry_size)
+ {
+ uint32_t new_entry_size = entry_size + sizeof(uint32_t);
+ *((uint32_t*)(((char*)fte) + entry_size)) = htonl(new_entry_size);
+ entry_size = new_entry_size;
+ }
+
+ return entry_size;
+}
+
+//retrieve a ppe trace entry from a ppe trace buffer
+size_t ppe_get_entry(PkTraceBuffer* tb, uint32_t offset, LargestPpeEntry* pte)
+{
+ uint32_t mask = ntohs(tb->size) - 1;
+ PkTraceEntryFooter* footer;
+ size_t entry_size;
+ size_t parm_size;
+ char* dest = (char*)pte;
+ uint32_t format;
+ uint32_t start_index;
+ uint32_t bytes_left;
+ uint32_t bytes_to_copy;
+
+ //Find the footer in the circular buffer
+ footer = (PkTraceEntryFooter*)(&tb->cb[(offset - sizeof(PkTraceEntryFooter)) & mask]);
+
+ //always correct endianess for the time and string id words
+ pte->footer.generic.time_format.word32 = ntohl(footer->generic.time_format.word32);
+ pte->footer.generic.string_id = ntohs(footer->generic.string_id);
+
+ //only need to byte swap the parm16 value if this is a tiny format
+ pte->footer.generic.parm16 = footer->generic.parm16;
+
+ //use footer data to determine the length of the binary data or parameters
+ format = PK_GET_TRACE_FORMAT(pte->footer.generic.time_format.word32);
+ switch(format)
+ {
+ case PK_TRACE_FORMAT_TINY:
+ pte->footer.generic.parm16 = ntohs(pte->footer.generic.parm16);
+ parm_size = 0;
+ entry_size = sizeof(PkTraceEntryFooter);
+ break;
+
+ case PK_TRACE_FORMAT_BIG:
+ parm_size = pte->footer.generic.bytes_or_parms_count * sizeof(uint32_t);
+ entry_size = sizeof(PkTraceEntryFooter);
+ break;
+
+ case PK_TRACE_FORMAT_BINARY:
+ parm_size = pte->footer.generic.bytes_or_parms_count;
+ entry_size = sizeof(PkTraceEntryFooter);
+ break;
+
+ default:
+ entry_size = 0;
+ parm_size = 0;
+ break;
+ }
+
+ //pad to 8 byte boundary
+ parm_size = (parm_size + 7) & ~0x00000007ul;
+
+ //add the parameter size to the total entry size
+ entry_size += parm_size;
+
+ //copy the entry from the circular buffer to pte
+ start_index = (offset - entry_size) & mask;
+ bytes_left = ntohs(tb->size) - start_index;
+
+ //only copy up to the end of the circular buffer
+ if(parm_size < bytes_left)
+ {
+ bytes_to_copy = parm_size;
+ }
+ else
+ {
+ bytes_to_copy = bytes_left;
+ }
+
+ dest += sizeof(LargestPpeEntry) - entry_size;
+ memcpy(dest, &tb->cb[start_index], bytes_to_copy);
+
+ //now copy the rest of the data starting from the beginning of the
+ //circular buffer.
+ if(bytes_to_copy < parm_size)
+ {
+ memcpy(dest + bytes_to_copy, tb->cb, parm_size - bytes_to_copy);
+ }
+
+ //return the size of the entry
+ return entry_size;
+}
+
+//convert a ppe trace buffer to an fsp trace buffer
+int ppe2fsp(void* in, size_t in_size, void* out, size_t* io_size)
+{
+ PkTraceBuffer* ptb = (PkTraceBuffer*)in;
+ trace_buf_head_t* ftb = (trace_buf_head_t*)out;
+ uint32_t ppe_bytes_left;
+ uint32_t fsp_bytes_left;
+ int rc = 0;
+ uint32_t ptb_offset;
+ uint64_t ppe_time64;
+ uint32_t fte_size, pte_size;
+ uint32_t fsp_te_count = 0;
+ uint32_t time_diff32, prev_time32, new_time32;
+ PkTraceGeneric* pte_footer;
+ largest_fsp_entry_t fte;
+ LargestPpeEntry pte;
+ uint64_t time_adj64;
+
+ do
+ {
+ if(!ptb || !ftb || !io_size)
+ {
+ rc = P2F_NULL_POINTER;
+ break;
+ }
+
+ if(ntohs(ptb->version) != PK_TRACE_VERSION)
+ {
+ rc = P2F_INVALID_VERSION;
+ break;
+ }
+
+ //check that the input buffer is large enough to have a ppe trace buffer
+ if(in_size < (((uintptr_t)(&ptb->cb[0])) - (uintptr_t)(ptb)))
+ {
+ rc = P2F_INPUT_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ //initialize some locals
+ fsp_bytes_left = *io_size - sizeof(trace_buf_head_t);
+ ppe_bytes_left = ntohs(ptb->size);
+ ptb_offset = ntohl(ptb->state.offset);
+ if(htonl(1) == 1)
+ {
+ time_adj64 = ptb->time_adj64;
+ }
+ else
+ {
+ time_adj64 = ntohl((uint32_t)(ptb->time_adj64 >> 32));
+ time_adj64 |= ((uint64_t)(ntohl((uint32_t)(ptb->time_adj64 & 0x00000000ffffffff)))) << 32;
+ }
+
+ //make sure the ppe buffer size is a power of two
+ if((ppe_bytes_left - 1) & ppe_bytes_left)
+ {
+ //size is not a power of two
+ rc = P2F_INVALID_INPUT_SIZE;
+ break;
+ }
+
+ //The ppe bytes field should always be a multiple of 8
+ if(ptb_offset & 0x7)
+ {
+ rc = P2F_INVALID_PPE_OFFSET;
+ break;
+ }
+
+ //make sure there is enough room for the fsp header
+ if(*io_size < sizeof(trace_buf_head_t))
+ {
+ rc = P2F_OUTPUT_BUFFER_TOO_SMALL;
+ break;
+ }
+
+
+ //initialize the fsp header
+ ftb->ver = TRACE_BUF_VERSION;
+ ftb->hdr_len = sizeof(trace_buf_head_t);
+ ftb->time_flg = TRAC_TIME_REAL;
+ ftb->endian_flg = 'B'; //big endian
+ memcpy(ftb->comp, ptb->image_str, sizeof(ftb->comp));
+ ftb->times_wrap = htonl(1);
+ ftb->size = htonl(sizeof(trace_buf_head_t) + sizeof(uint32_t));
+ ftb->next_free = htonl(sizeof(trace_buf_head_t));
+ ftb->extracted = htonl(0);
+ ftb->te_count = htonl(0);
+
+ //find the latest timestamp so that we can work back from there
+ ppe_time64 = ((uint64_t)(ntohl(ptb->state.tbu32) & 0xefffffff)) << 32;
+ pte_size = ppe_get_entry(ptb, ptb_offset, &pte);
+ prev_time32 = PK_GET_TRACE_TIME(pte.footer.generic.time_format.word32);
+ ppe_time64 |= prev_time32;
+
+ //process all of the input bytes one trace entry at a time
+ //from newest to oldest (backwards) until we run out of input bytes or
+ //we run out of output space.
+ while(1)
+ {
+ //check if we have enough data for a ppe footer
+ if(ppe_bytes_left < sizeof(PkTraceEntryFooter))
+ {
+ break;
+ }
+
+ //get the next ppe entry
+ pte_size = ppe_get_entry(ptb, ptb_offset, &pte);
+
+ //Stop if there are no more entries to retrieve from the ppe trace buffer
+ if(!pte_size)
+ {
+ break;
+ }
+ pte_footer = &pte.footer.generic;
+
+ //mark the entry as incomplete if we didn't have enough data
+ //for the entire entry
+ if(pte_size > ppe_bytes_left)
+ {
+ pte_footer->complete = 0;
+ ppe_bytes_left = 0;
+ }
+ else
+ {
+ ppe_bytes_left -= pte_size;
+ ptb_offset -= pte_size;
+ }
+
+ //Calculate the 64 bit timestamp for this entry....
+ //On PPE, getting the timestamp is not done atomically with writing
+ //the entry to the buffer. This means that an entry with an older
+ //timestamp could possibly be added to the buffer after an entry
+ //with a newer timestamp. Detect this condition by checking if the
+ //time difference is bigger than the max difference. The max
+ //difference is enforced by the PPE having a trace added on a
+ //shorter time boundary (using a timer).
+ new_time32 = PK_GET_TRACE_TIME(pte_footer->time_format.word32);
+ time_diff32 = prev_time32 - new_time32;
+
+ if(time_diff32 > ntohl(ptb->max_time_change))
+ {
+ time_diff32 = new_time32 - prev_time32;
+ ppe_time64 += time_diff32;
+ }
+ else
+ {
+ ppe_time64 -= time_diff32;
+ }
+
+ //save off the lower 32bit timestamp for the next iteration
+ prev_time32 = new_time32;
+
+ //convert the ppe trace entry to an fsp trace entry
+ fte_size = pte2fte(ptb, &pte, pte_size, &fte, ppe_time64 + time_adj64);
+
+ //fit as much of the entry into the fsp trace buffer as possible
+ fsp_put_entry(ftb, &fte, fte_size, fsp_bytes_left);
+
+ //update the fsp trace entry count
+ fsp_te_count++;
+
+ //stop if there is no more room left in the fsp trace buffer
+ if(fte_size >= fsp_bytes_left)
+ {
+ fsp_bytes_left = 0;
+ ftb->times_wrap = htonl(1);
+ break;
+ }
+ else
+ {
+ fsp_bytes_left -= fte_size;
+ }
+ }//while(1)
+
+
+ //shift the trace data up if there is space to do so
+ if(fsp_bytes_left)
+ {
+ char* dest = ((char*)ftb) + sizeof(trace_buf_head_t);
+ char* src = dest + fsp_bytes_left;
+ size_t data_size = *io_size - sizeof(trace_buf_head_t) - fsp_bytes_left;
+ memmove(dest, src, data_size);
+ }
+
+ //update the fsp header to reflect the true size and entry count
+ ftb->te_count = htonl(fsp_te_count);
+
+ //inform the caller of how many bytes were actually used
+ *io_size -= fsp_bytes_left;
+
+ //shrink the size field to what we actually ended up using
+ ftb->size = htonl(*io_size);
+
+ }while(0);
+
+ return rc;
+}
+
+
+
OpenPOWER on IntegriCloud