diff options
Diffstat (limited to 'sideband/src/pt_sb_session.c')
-rw-r--r-- | sideband/src/pt_sb_session.c | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/sideband/src/pt_sb_session.c b/sideband/src/pt_sb_session.c new file mode 100644 index 0000000000000..3b299838878f2 --- /dev/null +++ b/sideband/src/pt_sb_session.c @@ -0,0 +1,623 @@ +/* + * Copyright (c) 2017-2019, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pt_sb_session.h" +#include "pt_sb_context.h" +#include "pt_sb_decoder.h" + +#include "libipt-sb.h" +#include "intel-pt.h" + +#include <string.h> +#include <stdlib.h> + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +# define snprintf _snprintf_c +#endif + + +struct pt_sb_session *pt_sb_alloc(struct pt_image_section_cache *iscache) +{ + struct pt_sb_session *session; + struct pt_image *kernel; + + kernel = pt_image_alloc("kernel"); + if (!kernel) + return NULL; + + session = malloc(sizeof(*session)); + if (!session) { + pt_image_free(kernel); + return NULL; + } + + memset(session, 0, sizeof(*session)); + session->iscache = iscache; + session->kernel = kernel; + + return session; +} + +static void pt_sb_free_decoder(struct pt_sb_decoder *decoder) +{ + void (*dtor)(void *); + + if (!decoder) + return; + + dtor = decoder->dtor; + if (dtor) + dtor(decoder->priv); + + free(decoder); +} + +static void pt_sb_free_decoder_list(struct pt_sb_decoder *list) +{ + while (list) { + struct pt_sb_decoder *trash; + + trash = list; + list = trash->next; + + pt_sb_free_decoder(trash); + } +} + +void pt_sb_free(struct pt_sb_session *session) +{ + struct pt_sb_context *context; + + if (!session) + return; + + pt_sb_free_decoder_list(session->decoders); + pt_sb_free_decoder_list(session->waiting); + pt_sb_free_decoder_list(session->retired); + pt_sb_free_decoder_list(session->removed); + + context = session->contexts; + while (context) { + struct pt_sb_context *trash; + + trash = context; + context = trash->next; + + (void) pt_sb_ctx_put(trash); + } + + pt_image_free(session->kernel); + + free(session); +} + +struct pt_image_section_cache *pt_sb_iscache(struct pt_sb_session *session) +{ + if (!session) + return NULL; + + return session->iscache; +} + +struct pt_image *pt_sb_kernel_image(struct pt_sb_session *session) +{ + if (!session) + return NULL; + + return session->kernel; +} + +static int pt_sb_add_context_by_pid(struct pt_sb_context **pcontext, + struct pt_sb_session *session, uint32_t pid) +{ + struct pt_sb_context *context; + struct pt_image *kernel; + char iname[16]; + int errcode; + + if (!pcontext || !session) + return -pte_invalid; + + kernel = pt_sb_kernel_image(session); + if (!kernel) + return -pte_internal; + + memset(iname, 0, sizeof(iname)); + (void) snprintf(iname, sizeof(iname), "pid-%x", pid); + + context = pt_sb_ctx_alloc(iname); + if (!context) + return -pte_nomem; + + errcode = pt_image_copy(context->image, kernel); + if (errcode < 0) { + (void) pt_sb_ctx_put(context); + return errcode; + } + + context->next = session->contexts; + context->pid = pid; + + session->contexts = context; + *pcontext = context; + + return 0; +} + +int pt_sb_get_context_by_pid(struct pt_sb_context **context, + struct pt_sb_session *session, uint32_t pid) +{ + int status; + + if (!context || !session) + return -pte_invalid; + + status = pt_sb_find_context_by_pid(context, session, pid); + if (status < 0) + return status; + + if (*context) + return 0; + + return pt_sb_add_context_by_pid(context, session, pid); +} + +int pt_sb_find_context_by_pid(struct pt_sb_context **pcontext, + struct pt_sb_session *session, uint32_t pid) +{ + struct pt_sb_context *ctx; + + if (!pcontext || !session) + return -pte_invalid; + + for (ctx = session->contexts; ctx; ctx = ctx->next) { + if (ctx->pid == pid) + break; + } + + *pcontext = ctx; + + return 0; +} + +int pt_sb_remove_context(struct pt_sb_session *session, + struct pt_sb_context *context) +{ + struct pt_sb_context **pnext, *ctx; + + if (!session || !context) + return -pte_invalid; + + pnext = &session->contexts; + for (ctx = *pnext; ctx; pnext = &ctx->next, ctx = *pnext) { + if (ctx == context) + break; + } + + if (!ctx) + return -pte_nosync; + + *pnext = ctx->next; + + return pt_sb_ctx_put(ctx); +} + +int pt_sb_alloc_decoder(struct pt_sb_session *session, + const struct pt_sb_decoder_config *config) +{ + struct pt_sb_decoder *decoder; + + if (!session || !config) + return -pte_invalid; + + decoder = malloc(sizeof(*decoder)); + if (!decoder) + return -pte_nomem; + + memset(decoder, 0, sizeof(*decoder)); + decoder->next = session->waiting; + decoder->fetch = config->fetch; + decoder->apply = config->apply; + decoder->print = config->print; + decoder->dtor = config->dtor; + decoder->priv = config->priv; + decoder->primary = config->primary; + + session->waiting = decoder; + + return 0; +} + +/* Add a new decoder to a list of decoders. + * + * Decoders in @list are ordered by their @tsc (ascending) and @list does not + * contain @decoder. Find the right place for @decoder at add it to @list. + * + * Returns zero on success, a negative error code otherwise. + */ +static int pt_sb_add_decoder(struct pt_sb_decoder **list, + struct pt_sb_decoder *decoder) +{ + struct pt_sb_decoder *cand; + uint64_t tsc; + + if (!list || !decoder || decoder->next) + return -pte_internal; + + tsc = decoder->tsc; + for (cand = *list; cand; list = &cand->next, cand = *list) { + if (tsc <= cand->tsc) + break; + } + + decoder->next = cand; + *list = decoder; + + return 0; +} + +static int pt_sb_fetch(struct pt_sb_session *session, + struct pt_sb_decoder *decoder) +{ + int (*fetch)(struct pt_sb_session *, uint64_t *, void *); + + if (!decoder) + return -pte_internal; + + fetch = decoder->fetch; + if (!fetch) + return -pte_bad_config; + + return fetch(session, &decoder->tsc, decoder->priv); +} + +static int pt_sb_print(struct pt_sb_session *session, + struct pt_sb_decoder *decoder, FILE *stream, + uint32_t flags) +{ + int (*print)(struct pt_sb_session *, FILE *, uint32_t, void *); + + if (!decoder) + return -pte_internal; + + print = decoder->print; + if (!print) + return -pte_bad_config; + + return print(session, stream, flags, decoder->priv); +} + +static int pt_sb_apply(struct pt_sb_session *session, struct pt_image **image, + struct pt_sb_decoder *decoder, + const struct pt_event *event) +{ + int (*apply)(struct pt_sb_session *, struct pt_image **, + const struct pt_event *, void *); + + if (!decoder || !event) + return -pte_internal; + + apply = decoder->apply; + if (!apply) + return -pte_bad_config; + + if (!decoder->primary) + image = NULL; + + return apply(session, image, event, decoder->priv); +} + +int pt_sb_init_decoders(struct pt_sb_session *session) +{ + struct pt_sb_decoder *decoder; + + if (!session) + return -pte_invalid; + + decoder = session->waiting; + while (decoder) { + int errcode; + + session->waiting = decoder->next; + decoder->next = NULL; + + errcode = pt_sb_fetch(session, decoder); + if (errcode < 0) { + /* Fetch errors remove @decoder. In this case, they + * prevent it from being added in the first place. + */ + pt_sb_free_decoder(decoder); + } else { + errcode = pt_sb_add_decoder(&session->decoders, + decoder); + if (errcode < 0) + return errcode; + } + + decoder = session->waiting; + } + + return 0; +} + +/* Copy an event provided by an unknown version of libipt. + * + * Copy at most @size bytes of @uevent into @event and zero-initialize any + * additional bytes in @event not covered by @uevent. + * + * Returns zero on success, a negative error code otherwise. + */ +static int pt_sb_event_from_user(struct pt_event *event, + const struct pt_event *uevent, size_t size) +{ + if (!event || !uevent) + return -pte_internal; + + if (size < offsetof(struct pt_event, reserved)) + return -pte_invalid; + + /* Ignore fields in the user's event we don't know; zero out fields the + * user didn't know about. + */ + if (sizeof(*event) < size) + size = sizeof(*event); + else + memset(((uint8_t *) event) + size, 0, sizeof(*event) - size); + + /* Copy (portions of) the user's event. */ + memcpy(event, uevent, size); + + return 0; +} + +static int pt_sb_event_present(struct pt_sb_session *session, + struct pt_image **image, + struct pt_sb_decoder **pnext, + const struct pt_event *event) +{ + struct pt_sb_decoder *decoder; + int errcode; + + if (!session || !pnext) + return -pte_internal; + + decoder = *pnext; + while (decoder) { + errcode = pt_sb_apply(session, image, decoder, event); + if (errcode < 0) { + struct pt_sb_decoder *trash; + + trash = decoder; + decoder = trash->next; + *pnext = decoder; + + trash->next = session->removed; + session->removed = trash; + continue; + } + + pnext = &decoder->next; + decoder = *pnext; + } + + return 0; +} + +int pt_sb_event(struct pt_sb_session *session, struct pt_image **image, + const struct pt_event *uevent, size_t size, FILE *stream, + uint32_t flags) +{ + struct pt_sb_decoder *decoder; + struct pt_event event; + int errcode; + + if (!session || !uevent) + return -pte_invalid; + + errcode = pt_sb_event_from_user(&event, uevent, size); + if (errcode < 0) + return errcode; + + /* In the initial round, we present the event to all decoders with + * records for a smaller or equal timestamp. + * + * We only need to look at the first decoder. We remove it from the + * list and ask it to apply the event. Then, we ask it to fetch the + * next record and re-add it to the list according to that next record's + * timestamp. + */ + for (;;) { + decoder = session->decoders; + if (!decoder) + break; + + /* We don't check @event.has_tsc to support sideband + * correlation based on relative (non-wall clock) time. + */ + if (event.tsc < decoder->tsc) + break; + + session->decoders = decoder->next; + decoder->next = NULL; + + if (stream) { + errcode = pt_sb_print(session, decoder, stream, flags); + if (errcode < 0) { + decoder->next = session->removed; + session->removed = decoder; + continue; + } + } + + errcode = pt_sb_apply(session, image, decoder, &event); + if (errcode < 0) { + decoder->next = session->removed; + session->removed = decoder; + continue; + } + + errcode = pt_sb_fetch(session, decoder); + if (errcode < 0) { + if (errcode == -pte_eos) { + decoder->next = session->retired; + session->retired = decoder; + } else { + decoder->next = session->removed; + session->removed = decoder; + } + + continue; + } + + errcode = pt_sb_add_decoder(&session->decoders, decoder); + if (errcode < 0) + return errcode; + } + + /* In the second round, we present the event to all decoders. + * + * This allows decoders to postpone actions until an appropriate event, + * e.g entry into or exit from the kernel. + */ + errcode = pt_sb_event_present(session, image, &session->decoders, + &event); + if (errcode < 0) + return errcode; + + return pt_sb_event_present(session, image, &session->retired, &event); +} + +int pt_sb_dump(struct pt_sb_session *session, FILE *stream, uint32_t flags, + uint64_t tsc) +{ + struct pt_sb_decoder *decoder; + int errcode; + + if (!session || !stream) + return -pte_invalid; + + for (;;) { + decoder = session->decoders; + if (!decoder) + break; + + if (tsc < decoder->tsc) + break; + + session->decoders = decoder->next; + decoder->next = NULL; + + errcode = pt_sb_print(session, decoder, stream, flags); + if (errcode < 0) { + decoder->next = session->removed; + session->removed = decoder; + continue; + } + + errcode = pt_sb_fetch(session, decoder); + if (errcode < 0) { + decoder->next = session->removed; + session->removed = decoder; + continue; + } + + errcode = pt_sb_add_decoder(&session->decoders, decoder); + if (errcode < 0) + return errcode; + } + + return 0; +} + +pt_sb_ctx_switch_notifier_t * +pt_sb_notify_switch(struct pt_sb_session *session, + pt_sb_ctx_switch_notifier_t *notifier, void *priv) +{ + pt_sb_ctx_switch_notifier_t *old; + + if (!session) + return NULL; + + old = session->notify_switch_to; + + session->notify_switch_to = notifier; + session->priv_switch_to = priv; + + return old; +} + +pt_sb_error_notifier_t * +pt_sb_notify_error(struct pt_sb_session *session, + pt_sb_error_notifier_t *notifier, void *priv) +{ + pt_sb_error_notifier_t *old; + + if (!session) + return NULL; + + old = session->notify_error; + + session->notify_error = notifier; + session->priv_error = priv; + + return old; +} + +int pt_sb_error(const struct pt_sb_session *session, int errcode, + const char *filename, uint64_t offset) +{ + pt_sb_error_notifier_t *notifier; + + if (!session) + return -pte_internal; + + notifier = session->notify_error; + if (!notifier) + return 0; + + return notifier(errcode, filename, offset, session->priv_error); +} + +const char *pt_sb_errstr(enum pt_sb_error_code errcode) +{ + switch (errcode) { + case ptse_ok: + return "OK"; + + case ptse_lost: + return "sideband lost"; + + case ptse_trace_lost: + return "trace lost"; + + case ptse_section_lost: + return "image section lost"; + } + + return "bad errcode"; +} |