summaryrefslogtreecommitdiff
path: root/sideband/src/pt_sb_session.c
diff options
context:
space:
mode:
Diffstat (limited to 'sideband/src/pt_sb_session.c')
-rw-r--r--sideband/src/pt_sb_session.c623
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";
+}