summaryrefslogtreecommitdiff
path: root/utils/tracepoints/analyze-ipa-trace.py
blob: 50fbbf4299705dfc9b0ee774e0a03e1f5b754dee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2020, Google Inc.
#
# Author: Paul Elder <paul.elder@ideasonboard.com>
#
# analyze-ipa-trace.py - Example of how to extract information from libcamera lttng traces

import argparse
import bt2
import statistics as stats
import sys

# pipeline -> {function -> stack(timestamps)}
timestamps = {}

# pipeline:function -> samples[]
samples = {}

def main(argv):
    parser = argparse.ArgumentParser(
            description='A simple analysis script to get statistics on time taken for IPA calls')
    parser.add_argument('-p', '--pipeline', type=str,
                        help='Name of pipeline to filter for')
    parser.add_argument('trace_path', type=str,
                        help='Path to lttng trace (eg. ~/lttng-traces/demo-20201029-184003)')
    args = parser.parse_args(argv[1:])

    traces = bt2.TraceCollectionMessageIterator(args.trace_path)
    for msg in traces:
        if type(msg) is not bt2._EventMessageConst or \
           'pipeline_name' not in msg.event.payload_field or \
           (args.pipeline is not None and \
            msg.event.payload_field['pipeline_name'] != args.pipeline):
            continue

        pipeline = msg.event.payload_field['pipeline_name']
        event = msg.event.name
        func = msg.event.payload_field['function_name']
        timestamp_ns = msg.default_clock_snapshot.ns_from_origin

        if event == 'libcamera:ipa_call_begin':
            if pipeline not in timestamps:
                timestamps[pipeline] = {}
            if func not in timestamps[pipeline]:
                timestamps[pipeline][func] = []
            timestamps[pipeline][func].append(timestamp_ns)

        if event == 'libcamera:ipa_call_end':
            ts = timestamps[pipeline][func].pop()
            key = f'{pipeline}:{func}'
            if key not in samples:
                samples[key] = []
            samples[key].append(timestamp_ns - ts)

    # Compute stats
    rows = []
    rows.append(['pipeline:function', 'min', 'max', 'mean', 'stddev'])
    for k, v in samples.items():
        mean = int(stats.mean(v))
        stddev = int(stats.stdev(v))
        minv = min(v)
        maxv = max(v)
        rows.append([k, str(minv), str(maxv), str(mean), str(stddev)])

    # Get maximum string width for every column
    widths = []
    for i in range(len(rows[0])):
        widths.append(max([len(row[i]) for row in rows]))

    # Print stats table
    for row in rows:
        fmt = [row[i].rjust(widths[i]) for i in range(1, 5)]
        print('{} {} {} {} {}'.format(row[0].ljust(widths[0]), *fmt))

if __name__ == '__main__':
    sys.exit(main(sys.argv))
h" #include <stdint.h> #include <string.h> #include <libcamera/base/log.h> /** * \file byte_stream_buffer.h * \brief Managed memory container for serialized data */ namespace libcamera { LOG_DEFINE_CATEGORY(Serialization) /** * \class ByteStreamBuffer * \brief Wrap a memory buffer and provide sequential data read and write * * The ByteStreamBuffer class wraps a memory buffer and exposes sequential read * and write operation with integrated boundary checks. Access beyond the end * of the buffer are blocked and logged, allowing error checks to take place at * the of of access operations instead of at each access. This simplifies * serialization and deserialization of data. * * A byte stream buffer is created with a base memory pointer and a size. If the * memory pointer is const, the buffer operates in read-only mode, and write * operations are denied. Otherwise the buffer operates in write-only mode, and * read operations are denied. * * Once a buffer is created, data is read or written with read() and write() * respectively. Access is strictly sequential, the buffer keeps track of the * current access location and advances it automatically. Reading or writing * the same location multiple times is thus not possible. Bytes may also be * skipped with the skip() function. * * * The ByteStreamBuffer also supports carving out pieces of memory into other * ByteStreamBuffer instances. Like a read or write operation, a carveOut() * advances the internal access location, but allows the carved out memory to * be accessed at a later time. * * All accesses beyond the end of the buffer (read, write, skip or carve out) * are blocked. The first of such accesses causes a message to be logged, and * the buffer being marked as having overflown. If the buffer has been carved * out from a parent buffer, the parent buffer is also marked as having * overflown. Any later access on an overflown buffer is blocked. The buffer * overflow status can be checked with the overflow() function. */ /** * \brief Construct a read ByteStreamBuffer from the memory area \a base * of \a size * \param[in] base The address of the memory area to wrap * \param[in] size The size of the memory area to wrap */ ByteStreamBuffer::ByteStreamBuffer(const uint8_t *base, size_t size) : parent_(nullptr), base_(base), size_(size), overflow_(false), read_(base), write_(nullptr) { } /** * \brief Construct a write ByteStreamBuffer from the memory area \a base * of \a size * \param[in] base The address of the memory area to wrap * \param[in] size The size of the memory area to wrap */ ByteStreamBuffer::ByteStreamBuffer(uint8_t *base, size_t size) : parent_(nullptr), base_(base), size_(size), overflow_(false), read_(nullptr), write_(base) { } /** * \brief Construct a ByteStreamBuffer from the contents of \a other using move * semantics * \param[in] other The other buffer * * After the move construction the \a other buffer is invalidated. Any attempt * to access its contents will be considered as an overflow. */ ByteStreamBuffer::ByteStreamBuffer(ByteStreamBuffer &&other) { *this = std::move(other); } /** * \brief Replace the contents of the buffer with those of \a other using move * semantics * \param[in] other The other buffer * * After the assignment the \a other buffer is invalidated. Any attempt to * access its contents will be considered as an overflow. */ ByteStreamBuffer &ByteStreamBuffer::operator=(ByteStreamBuffer &&other) { parent_ = other.parent_; base_ = other.base_; size_ = other.size_; overflow_ = other.overflow_; read_ = other.read_; write_ = other.write_; other.parent_ = nullptr; other.base_ = nullptr; other.size_ = 0; other.overflow_ = false; other.read_ = nullptr; other.write_ = nullptr; return *this; } /** * \fn ByteStreamBuffer::base() * \brief Retrieve a pointer to the start location of the managed memory buffer * \return A pointer to the managed memory buffer */ /** * \fn ByteStreamBuffer::offset() * \brief Retrieve the offset of the current access location from the base * \return The offset in bytes */ /** * \fn ByteStreamBuffer::size() * \brief Retrieve the size of the managed memory buffer * \return The size of managed memory buffer */ /** * \fn ByteStreamBuffer::overflow() * \brief Check if the buffer has overflown * \return True if the buffer has overflow, false otherwise */ void ByteStreamBuffer::setOverflow() { if (parent_) parent_->setOverflow(); overflow_ = true; } /** * \brief Carve out an area of \a size bytes into a new ByteStreamBuffer * \param[in] size The size of the newly created memory buffer * * This function carves out an area of \a size bytes from the buffer into a new * ByteStreamBuffer, and returns the new buffer. It operates identically to a * read or write access from the point of view of the current buffer, but allows * the new buffer to be read or written at a later time after other read or * write accesses on the current buffer. * * \return A newly created ByteStreamBuffer of \a size */ ByteStreamBuffer ByteStreamBuffer::carveOut(size_t size) { if (!size_ || overflow_) return ByteStreamBuffer(static_cast<const uint8_t *>(nullptr), 0); const uint8_t *curr = read_ ? read_ : write_; if (curr + size > base_ + size_) { LOG(Serialization, Error) << "Unable to reserve " << size << " bytes"; setOverflow(); return ByteStreamBuffer(static_cast<const uint8_t *>(nullptr), 0); } if (read_) { ByteStreamBuffer b(read_, size); b.parent_ = this; read_ += size; return b; } else { ByteStreamBuffer b(write_, size); b.parent_ = this; write_ += size; return b; } } /** * \brief Skip \a size bytes from the buffer * \param[in] size The number of bytes to skip * * This function skips the next \a size bytes from the buffer. * * \return 0 on success, a negative error code otherwise * \retval -ENOSPC no more space is available in the managed memory buffer */ int ByteStreamBuffer::skip(size_t size) { if (overflow_) return -ENOSPC; const uint8_t *curr = read_ ? read_ : write_; if (curr + size > base_ + size_) { LOG(Serialization, Error) << "Unable to skip " << size << " bytes"; setOverflow(); return -ENOSPC; } if (read_) { read_ += size; } else { memset(write_, 0, size); write_ += size; } return 0; } /** * \fn template<typename T> int ByteStreamBuffer::read(T *t) * \brief Read data from the managed memory buffer into \a t * \param[out] t Pointer to the memory containing the read data * \return 0 on success, a negative error code otherwise * \retval -EACCES attempting to read from a write buffer * \retval -ENOSPC no more space is available in the managed memory buffer */ /** * \fn template<typename T> int ByteStreamBuffer::read(const Span<T> &data) * \brief Read data from the managed memory buffer into Span \a data * \param[out] data Span representing the destination memory * \return 0 on success, a negative error code otherwise * \retval -EACCES attempting to read from a write buffer * \retval -ENOSPC no more space is available in the managed memory buffer */ /** * \fn template<typename T> const T *ByteStreamBuffer::read(size_t count) * \brief Read data from the managed memory buffer without performing a copy * \param[in] count Number of data items to read * * This function reads \a count elements of type \a T from the buffer. Unlike * the other read variants, it doesn't copy the data but returns a pointer to * the first element. If data can't be read for any reason (usually due to * reading more data than available), the function returns nullptr. * * \return A pointer to the data on success, or nullptr otherwise */ /** * \fn template<typename T> int ByteStreamBuffer::write(const T *t) * \brief Write \a t to the managed memory buffer * \param[in] t The data to write to memory * \return 0 on success, a negative error code otherwise * \retval -EACCES attempting to write to a read buffer