From patchwork Fri Jul 28 19:04:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stevie Alvarez X-Patchwork-Id: 13332592 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 45DCDC001E0 for ; Fri, 28 Jul 2023 19:05:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229740AbjG1TFp (ORCPT ); Fri, 28 Jul 2023 15:05:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36262 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230026AbjG1TFo (ORCPT ); Fri, 28 Jul 2023 15:05:44 -0400 Received: from mail-io1-xd33.google.com (mail-io1-xd33.google.com [IPv6:2607:f8b0:4864:20::d33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DE9DA35A5 for ; Fri, 28 Jul 2023 12:05:40 -0700 (PDT) Received: by mail-io1-xd33.google.com with SMTP id ca18e2360f4ac-785d738d3feso59189839f.0 for ; Fri, 28 Jul 2023 12:05:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1690571140; x=1691175940; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=flKo1bfzWqYLcLMKV0yOYhv8ub7wqsUxP/yWzj7bOTk=; b=ox7PuoLrK8cH/LsJzvI4GdM7MP6GyQai9mJM/TTM2HscxjPCxqru3B7UMUcq9X1bko El32PhCx6S9cb+Su0UcGbwPSs9De//IqA7FN0naw7NSLijyXbx34o4oSeX7/PwUWGc4Z iqL4fIJzKpFjXEtMl+6Bslo95furdH0Q11DSa6M5xG/h8WfzmlQqD/uJADZiRNSmxj0m 6UTn57FY8jwY7N4xnA8XnudSciH6z2KwR0ckKJpv0sA7r6wSm6CaekRk46dRQvkFCbec RM3qprGrhj63mw92UShv+Rx7dYxZFoHnfCqO54lv/XzyZNroFj0y6IAmYujdp1iakLLs xmxQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690571140; x=1691175940; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=flKo1bfzWqYLcLMKV0yOYhv8ub7wqsUxP/yWzj7bOTk=; b=D9gvauIb5dLM349whK654jVZmzNknLkzX/3pLyIVDPAe4+Sn3rr0J0Cl676icWwG8d vEroUUk40dO1uiiEKr/iw3Zs8qaaGoymcARe2eYLu7BB58QUbd9S+m0+yYBnl6g4PLn9 kDMVDmK5wrluXce6z6U7iOQl8r5rDanLCrvDIaqFivVYMu46xHXTjkl58Xs2kK0SuWn4 RpfN9niEQaiOK9SpZ9uayh01NHQ00FuRYTllpoeOdgkXLVPjYxAzVlJEz+Q3LRTLOL+z DiB0O0j1raAMJBhI5gqV7bMHSKmSFG1QCnbZzEI8wDO3+ZE1jkzw7nYBUsoWC7RPpz6O lkfg== X-Gm-Message-State: ABy/qLa+myVWBBxB6hhcR+DIVj9sy+hXiaqL+WKakrVlMMLK8vf8J/kh vP6J2AnOkufyzCR06yeY7A6ZyAsugBMWNA== X-Google-Smtp-Source: APBJJlGjMbTidXGcby28QnK82NPCX53jjs2oz/ZZufPQ3iM9bdhap7Lq6IN8dlDl4OKf4JgQGQzYxA== X-Received: by 2002:a05:6e02:1609:b0:346:4f37:8a3 with SMTP id t9-20020a056e02160900b003464f3708a3mr389378ilu.5.1690571139800; Fri, 28 Jul 2023 12:05:39 -0700 (PDT) Received: from localhost.localdomain ([2a00:79e1:abc:1705:d317:f8ec:adf0:bb06]) by smtp.gmail.com with ESMTPSA id u22-20020a02aa96000000b0042b3a328ee0sm1268452jai.166.2023.07.28.12.05.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jul 2023 12:05:39 -0700 (PDT) From: Stevie Alvarez To: linux-trace-devel@vger.kernel.org Cc: "Stevie Alvarez (Google)" , Steven Rostedt , Ross Zwisler Subject: [PATCH 1/5] histograms: initial histograms interface Date: Fri, 28 Jul 2023 15:04:36 -0400 Message-ID: <20230728190515.23088-1-stevie.6strings@gmail.com> X-Mailer: git-send-email 2.41.0 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Stevie Alvarez (Google)" Initial header file for libtraceeval's histogram API. The interface provides a simple way of aggregating trace data and reading through said data. Signed-off-by: Stevie Alvarez (Google) --- include/histograms.h | 340 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 include/histograms.h diff --git a/include/histograms.h b/include/histograms.h new file mode 100644 index 0000000..b8cd53e --- /dev/null +++ b/include/histograms.h @@ -0,0 +1,340 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libtraceeval histogram interface. + * + * Copyright (C) 2023 Google Inc, Steven Rostedt + * Copyright (C) 2023 Google Inc, Stevie Alvarez + */ +#ifndef __LIBTRACEEVAL_HIST_H__ +#define __LIBTRACEEVAL_HIST_H__ + +#include +#include +#include + +// Data definition interfaces + +/** Data type distinguishers */ +enum traceeval_data_type { + TRACEEVAL_TYPE_NONE, + TRACEEVAL_TYPE_STRING, + TRACEEVAL_TYPE_NUMBER, + TRACEEVAL_TYPE_NUMBER_64, + TRACEEVAL_TYPE_NUMBER_32, + TRACEEVAL_TYPE_NUMBER_16, + TRACEEVAL_TYPE_NUMBER_8, + TRACEEVAL_TYPE_DYNAMIC +}; + +/** Statistics specification flags */ +enum traceeval_flags { + TRACEEVAL_FL_SIGNED = (1 << 0), + TRACEEVAL_FL_STATS = (1 << 1) +}; + +/** + * Trace data entry for a traceeval histogram + * Constitutes keys and values. + */ +union traceeval_data { + char *string; + struct traceeval_dynamic *dyn_data; + unsigned long number; + unsigned char number_8; + unsigned short number_16; + unsigned int number_32; + unsigned long long number_64; +}; + +/** + * Describes a struct traceeval_data instance + * Defines expectations for a corresponding traceeval_data instance for a + * traceeval histogram instance. Used to describe both keys and values. + * + * The id field is an optional value in case the user has multiple struct + * traceeval_type instances with type fields set to TRACEEVAL_TYPE_DYNAMIC, + * which each relate to distinct user defined struct traceeval_dynamic + * 'sub-types'. + * For flexibility, dyn_cmp() and dyn_release() take a struct traceeval_type + * instance. This allows the user to distingush between different sub-types of + * struct traceeeval_dynamic within a single callback function by examining the + * id field. This is not a required approach, merely one that is accomodated. + * + * dyn_cmp() is used to compare two struct traceeval_dynamic instances when a + * corresponding struct traceeval_type is reached with its type field set to + * TRACEEVAL_TYPE_DYNAMIC. It should return 0 on equality, 1 if the first + * argument is greater than the second, -1 for the otherway around, and -2 on + * error. + * + * dyn_release() is used during traceeval_release() to release a union + * traceeval_data's struct traceeval_dynamic field when the corresponding + * traceeval_type type is set to TRACEEVAL_TYPE_DYNAMIC. + */ +struct traceeval_type { + enum traceeval_data_type type; + char *name; + size_t flags; + size_t id; + void (*dyn_release)(struct traceeval_dynamic *, struct traceeval_type *); + int (*dyn_cmp)(struct traceeval_dynamic *, struct traceeval_dynamic *, + struct traceeval_type *); +}; + +/** Storage for atypical data */ +struct traceeval_dynamic { + size_t size; + void *data; +}; + +// Histogram interfaces + +/** Histogram */ +struct traceeval; + +/** + * traceeval_init - create a traceeval descriptor + * @keys: Defines the keys of the "histogram" + * @vals: Defines the vals of the "histogram" + * + * The caller still owns @keys and @vals. The caller is responsible for any + * necessary clean up of @keys and @vals. + * For any member of @keys or @vals that isn't of type TRACEEVAL_TYPE_NONE, + * the name field must be either a null-terminated string or NULL. For + * members of type TRACEEVAL_TYPE_NONE, the name is ignored. + * The @keys and @vals define how the traceeval instance will be populated. + * The @keys will be used by traceeval_query() to find an instance within + * the "historgram". Note, both the @keys and @vals array must end with: + * { .type = TRACEEVAL_TYPE_NONE }. + * + * @vals can be NULL or start with its type field as TRACEEVAL_TYPE_NONE to + * define the values of the histogram to be empty. If @keys is NULL or starts + * with { .type = TRACEEVAL_TYPE_NONE }, it is treated as an error to ensure + * the histogram is valid. + * + * Retruns the descriptor on success, or NULL on error. + */ +struct traceeval *traceeval_init(const struct traceeval_type *keys, + const struct traceeval_type *vals); + +/** + * traceeval_release - release a traceeval descriptor + * @teval: An instance of traceeval returned by traceeval_init() + * + * When the caller of traceeval_init() is done with the returned @teval, + * it must call traceeval_release(). + * This does not release any dynamically allocated data inserted by + * the user, although it will call any dyn_release() functions provided by + * the user from traceeval_init(). + */ +void traceeval_release(struct traceeval *teval); + +/** + * traceeval_insert - Insert an item into the traceeval descriptor + * @teval: The descriptor to insert into + * @keys: The list of keys that defines what is being inserted. + * @vals: The list of values that defines what is being inserted. + * + * Any dynamically allocated data is still owned by the caller; the + * responsibility of deallocating it lies on the caller. + * For all elements of @keys and @vals that correspond to a struct + * traceeval_type of type TRACEEVAL_TYPE_STRING, the string field must be set + * to either a null-terminated string or NULL. + * The @keys is an array that holds the data in the order of the keys + * passed into traceeval_init(). That is, if traceeval_init() had + * keys = { { .type = TRACEEVAL_STRING }, { .type = TRACEEVAL_NUMBER_8 }, + * { .type = TRACEEVAL_NONE } }; then the @keys array passed in must + * be a string (char *) followed by a 8 byte number (char). The @keys + * and @vals are only examined to where it expects data. That is, + * if the traceeval_init() keys had 3 items where the first two was defining + * data, and the last one was the TRACEEVAL_TYPE_NONE, then the @keys + * here only needs to be an array of 2, inserting the two items defined + * by traceeval_init(). The same goes for @vals. + * + * Returns 0 on success, and -1 on error. + */ +int traceeval_insert(struct traceeval *teval, + const union traceeval_data *keys, + const union traceeval_data *vals); + +/** + * traceeval_compare - Check equality between two traceeval instances + * @orig: The first traceeval instance + * @copy: The second traceeval instance + * + * This compares the values of the key definitions, value definitions, and + * inserted data between @orig and @copy in order. It does not compare + * by memory address, except for struct traceeval_type's dyn_release() and + * dyn_cmp() fields. + * + * Returns 0 if @orig and @copy are the same, 1 if not, and -1 on error. + */ +int traceeval_compare(struct traceeval *orig, struct traceeval *copy); + +// interface to queuery traceeval + +/** + * traceeval_query - Find the last instance defined by the keys + * @teval: The descriptor to search from + * @keys: A list of data to look for + * @results: A pointer to where to place the results (if found) + * + * This does a lookup for an instance within the traceeval data. + * The @keys is an array defined by the keys declared in traceeval_init(). + * The @keys will return an item that had the same keys when it was + * inserted by traceeval_insert(). The @keys here follow the same rules + * as the keys for traceeval_insert(). + * + * Note, when the caller is done with @results, it must call + * traceeval_results_release() on it. + * + * Returns 1 if found, 0 if not found, and -1 on error. + */ +int traceeval_query(struct traceeval *teval, + const union traceeval_data *keys, + union traceeval_data * const *results); + +/** Field name/descriptor for number of hits */ +#define TRACEEVAL_VAL_HITS ((const char *)(-1UL)) + +/** + * traceeval_find_key - find the index of a key + * @teval: The descriptor to find the key for + * @field: The name of the key to return the index for + * + * As the order of how keys are defined by traceeval_init(), it + * is important to know what index they are for when dealing with + * the other functions. + * + * Returns the index of the key with @field as the name. + * Return -1 if not found. + */ +int traceeval_find_key(struct traceeval *teval, const char *field); + +/** + * traceeval_find_val - find the index of a value + * @teval: The descriptor to find the value for + * @field: The name of the value to return the index for + * + * As the order of how values are defined by traceeval_init(), it + * is important to know what index they are for when dealing with + * the results array from traceeval_query(). In order to facilitate + * this, traceeval_find_val() will return the index for a given + * @field so that the caller does not have to keep track of it. + * + * Returns the index of the value with @field as the name that can be + * used to index the @results array from traceeval_query(). + * Return -1 if not found. + */ +int traceeval_find_val(struct traceeval *teval, const char *field); + +/** + * traceeval_results_release - Release the results return by traceeval_query() + * @teval: The descriptor used in traceeval_query() + * @results: The results returned by traceeval_query() + * + * The @results returned by traceeval_query() is owned by @teval, and + * how it manages it is implementation specific. The caller should not + * worry about it. When the caller of traceeval_query() is done with + * the @results, it must call traceeval_results_release() on it to + * allow traceeval to clean up its references. + */ +void traceeval_results_release(struct traceeval *teval, + const union traceeval_data *results); + +// Result iterator/histogram dump interfaces + +/** Iterator over aggregated data */ +struct traceeval_iterator; + +/** + * traceeval_iterator_get - get an iterator to read the data from traceeval + * @teval: The descriptor to read from + * + * This returns a descriptor that can be used to interate through all the + * data within @teval. + * + * Returns the iterator on success, NULL on error. + */ +struct traceeval_iterator *traceeval_iterator_get(struct traceeval *teval); + +/** + * traceeval_iterator_sort - Set how the iterator is sorted + * @iter: The iterator to set the sorting + * @sort_field: The field (defined by traceeval_init) to sort by. + * @level: The level of sorting. + * @ascending: Set to true to sort ascending order, or false to descending + * + * Sets how the iterator shall be sorted. @sort_field is the field to sort + * by and may be the name of either a key or a value. + * + * The @level should be zero the first time this is called to define the main sort + * field. If secondary sorting is to be done, then this function should be called + * again with @level as 1. For more levels of sorting just call this function + * for each level incrementing level each time. Note, if a level is missed, + * then this will return an error and sorting will not be done for that level. + * + * Returns 0 on success, -1 or error (including missing a level). + */ +int traceeval_iterator_sort(struct traceeval_iterator *iter, + const char *sort_field, int level, bool ascending); + +/** + * traceeval_iterator_next - Iterate through the data of a traceeval descriptor + * @iter: The iterator that holds the location and sorting of the data + * @keys: The current set of keys of the traceeval the @iter is sorting through + * + * This will iterate through all the data of the traceeval descriptor held + * by @iter in the sort pattern defined by traceeval_iterator_sort(). + * The @keys return is same as the data used to populate the data passed into + * traceveal_insert(). When the caller is done with @keys, it should be passed + * into traceeval_keys_release(). + * + * Returns 1 if it returned an item, 0 if there's no more data to return, + * and -1 on error. + */ +int traceeval_iterator_next(struct traceeval_iterator *iter, + const union traceeval_data **keys); + +/** + * traceeval_keys_release - Release the keys return by traceeval_iterator_next() + * @iter: The iterator used in traceeval_iterator_next(). + * @keys: The results returned by traceeval_iterator_next() + * + * The @keys returned by traceeval_iterator_next() is owned by @iter, and + * how it manages it is implementation specific. The caller should not + * worry about it. When the caller of traceeval_iterator_next() is done with + * the @keys, it must call traceeval_keys_release() on it to + * allow traceeval to clean up its references. + */ +void traceeval_keys_release(struct traceeval_iterator *iter, + const union traceeval_data *keys); + +// Statistic extraction + +/** Statistics about a given field */ +struct traceeval_stat { + unsigned long long max; + unsigned long long min; + unsigned long long total; + unsigned long long avg; + unsigned long long std; +}; + +/** + * traceeval_stat - Extract stats from a field marke a TRACEEVAL_FL_STATS + * @teval: The descriptor holding the traceeval information + * @keys: The list of keys to find the instance to get the stats on + * @field: The field to retreive the stats for + * @stats: A structure to place the stats into. + * + * This returns the stats of the the given @field. Note, if @field was + * not marked for recording stats with the TRACEEVAL_FL_STATS flag, or + * if no instance is found that has @keys, this will return an error. + * + * Returns 0 on success, -1 on error. + */ +int traceeval_stat(struct traceeval *teval, const union traceeval_data *keys, + const char *field, struct traceeval_stat *stat); + +#endif /* __LIBTRACEEVAL_HIST_H__ */ + From patchwork Fri Jul 28 19:04:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stevie Alvarez X-Patchwork-Id: 13332595 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E2C1FC001DF for ; Fri, 28 Jul 2023 19:05:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230026AbjG1TFq (ORCPT ); Fri, 28 Jul 2023 15:05:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36260 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229888AbjG1TFo (ORCPT ); Fri, 28 Jul 2023 15:05:44 -0400 Received: from mail-io1-xd2b.google.com (mail-io1-xd2b.google.com [IPv6:2607:f8b0:4864:20::d2b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 339E73AA8 for ; Fri, 28 Jul 2023 12:05:41 -0700 (PDT) Received: by mail-io1-xd2b.google.com with SMTP id ca18e2360f4ac-78374596182so99033939f.0 for ; Fri, 28 Jul 2023 12:05:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1690571140; x=1691175940; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=JMbb7Q3GMhiHEEnUcGGyQZpaC0Zkjt1iZeRWhkvmC1k=; b=LcURZXLQmHNhrJ4H8GEb0QBykD0FIuHSui+03uoRKnwbu9JFuwAQ2lUL0HR0KOPnIw A6KqtJ9aPIF71kFMIwVXpuJnt3cFevGOGaRaqhrYf49tbsurQXKmS6/pcucxNuKLgRwK hfR0UoD9WX7tYpGLAuHME4NZzqccJDf8SSELe+Lum+pMg0p1kFcJ0PuLOpjHdoxb5MVS 0Qr9M+4VgtLVJUJhVXMkHlxttr7eU2Zc7+nMvgCQBsvo7nKf3U+YxDFKTavCn1egcUR6 v9EZhZgK9F93ecUIAmmcun8J3tsimrQyelJegmHbxzSuMpdxFJ/t8ZLskoZd90cwvomM +Lig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690571140; x=1691175940; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JMbb7Q3GMhiHEEnUcGGyQZpaC0Zkjt1iZeRWhkvmC1k=; b=W01fk8ayYq4gzNNIU8jnMPVzHPW9IOdk3mwLSvRNRnbIpxh2seyf95NGs5qcAmqWM6 OMUa9Evs6RWKEWDTZWnzZ2C2PrBObtaBW7Pu2myZlTC0a+sa3VhOgNmV3cMNlKdiNuPY 7Q/myFUnrqBas8nc1fenk9gIlp+bxaXLjhK1WDHUyLLbz0ItAUEZdBi3Qpbk0CR7+LAb Gphw5GW3U148tKaAuXa8mn3ULRdzBLQ9HnI9jScTYPndo8LndVp7HgteFkEG+FD5DgS1 AW1FdkvH7qBLiQiKx28L5C2HTgsaUYznz5S99xXeNaT+5boUSNJSUO9vhE5e9gMmLdPU GAsw== X-Gm-Message-State: ABy/qLYyzwR7geYiSVxxNvNR0TwgxFh1Q3shLqoEcZo5il9JqrOd5Y7p Odl9PohCG6nssImD5KY5fzl81YxAZA1uWg== X-Google-Smtp-Source: APBJJlHp5Q4CYHpJSDn2T1Diq4BoPIMIOrNfIpO+/KJSzP2gKB01zaeMlpJZG/kiiUq9N7hkfYNsZA== X-Received: by 2002:a6b:5903:0:b0:785:d0d7:3304 with SMTP id n3-20020a6b5903000000b00785d0d73304mr439961iob.14.1690571140404; Fri, 28 Jul 2023 12:05:40 -0700 (PDT) Received: from localhost.localdomain ([2a00:79e1:abc:1705:d317:f8ec:adf0:bb06]) by smtp.gmail.com with ESMTPSA id u22-20020a02aa96000000b0042b3a328ee0sm1268452jai.166.2023.07.28.12.05.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jul 2023 12:05:40 -0700 (PDT) From: Stevie Alvarez To: linux-trace-devel@vger.kernel.org Cc: "Stevie Alvarez (Google)" , Steven Rostedt , Ross Zwisler Subject: [PATCH 2/5] histograms: traceeval initialize Date: Fri, 28 Jul 2023 15:04:37 -0400 Message-ID: <20230728190515.23088-2-stevie.6strings@gmail.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230728190515.23088-1-stevie.6strings@gmail.com> References: <20230728190515.23088-1-stevie.6strings@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Stevie Alvarez (Google)" traceeval_init() creates a new struct traceeval instance with regards to the struct traceeval_type array arguments keys and vals. These arrays define the structure of the histogram, with each describing the expected structure of inserted arrays of union traceeval_data. The keys and vals arguments are copied on the heap to ensure that the struct traceeval instance has access to the definition regardless of how the user initialized keys and vals. Signed-off-by: Stevie Alvarez (Google) --- Makefile | 2 +- src/Makefile | 1 + src/histograms.c | 285 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 src/histograms.c diff --git a/Makefile b/Makefile index 4a24d5a..3ea051c 100644 --- a/Makefile +++ b/Makefile @@ -172,7 +172,7 @@ libs: $(LIBRARY_A) $(LIBRARY_SO) VALGRIND = $(shell which valgrind) UTEST_DIR = utest -UTEST_BINARY = trace-utest +UTEST_BINARY = eval-utest test: force $(LIBRARY_STATIC) ifneq ($(CUNIT_INSTALLED),1) diff --git a/src/Makefile b/src/Makefile index b4b6e52..b32a389 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,6 +4,7 @@ include $(src)/scripts/utils.mk OBJS = OBJS += trace-analysis.o +OBJS += histograms.o OBJS := $(OBJS:%.o=$(bdir)/%.o) diff --git a/src/histograms.c b/src/histograms.c new file mode 100644 index 0000000..13830e4 --- /dev/null +++ b/src/histograms.c @@ -0,0 +1,285 @@ + +/* SPDX-License-Identifier: MIT */ +/* + * libtraceeval histogram interface implementation. + * + * Copyright (C) 2023 Google Inc, Stevie Alvarez + */ + +#include +#include +#include +#include +#include + +/** + * Iterate over @keys, which should be an array of struct traceeval_type's, + * until reaching an instance of type TRACEEVAL_TYPE_NONE. + * @i should be a declared integer type. + */ +#define for_each_key(i, keys) \ + for (i = 0; (keys)[(i)].type != TRACEEVAL_TYPE_NONE; (i)++) + +/** A key-value pair */ +struct entry { + union traceeval_data *keys; + union traceeval_data *vals; +}; + +/** A table of key-value entries */ +struct hist_table { + struct entry *map; + size_t nr_entries; +}; + +/** Histogram */ +struct traceeval { + struct traceeval_type *def_keys; + struct traceeval_type *def_vals; + struct hist_table *hist; +}; + +/** Iterate over results of histogram */ +struct traceeval_iterator {}; // TODO + +/** + * Print error message. + * Additional arguments are used with respect to fmt. + */ +static void print_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +// TODO +int traceeval_compare(struct traceeval *orig, struct traceeval *copy) +{ + return -1; +} + +/** + * Resize a struct traceeval_type array to a size of @size + 1. + * + * Returns a pointer to the resized array, or NULL if the provided pointer was + * freed to due lack of memory. + */ +static struct traceeval_type *type_realloc(struct traceeval_type *defs, + size_t size) +{ + struct traceeval_type *tmp_defs = NULL; + tmp_defs = realloc(defs, + (size + 1) * sizeof(struct traceeval_type)); + if (!tmp_defs) + goto fail_type_realloc; + return tmp_defs; + +fail_type_realloc: + for (int i = 0; i < size; i++) { + if (defs[i].name) + free(defs[i].name); + } + free(defs); + return NULL; +} + +/** + * Clone traceeval_type array @defs to the heap. Must be terminated with + * an instance of type TRACEEVAL_TYPE_NONE. + * Returns NULL if @defs is NULL, or a name is not null terminated. + */ +static struct traceeval_type *type_alloc(const struct traceeval_type *defs) +{ + struct traceeval_type *new_defs = NULL; + char *name; + size_t size = 0; + + // Empty def is represented with single TRACEEVAL_TYPE_NONE + if (defs == NULL) { + if (!(new_defs = calloc(1, sizeof(struct traceeval_type)))) + goto fail_type_alloc; + new_defs->type = TRACEEVAL_TYPE_NONE; + return new_defs; + } + + for_each_key(size, defs) { + // Resize heap defs and clone + new_defs = type_realloc(new_defs, size); + if (!new_defs) + goto fail_type_alloc; + + // copy current def data to new_def + new_defs[size] = defs[size]; + new_defs[size].name = NULL; + // copy name to heap if it's not NULL or type NONE + if (defs[size].type != TRACEEVAL_TYPE_NONE) { + name = NULL; + if (!defs[size].name) + goto fail_type_alloc_name; + + name = strdup(defs[size].name); + if (!name) + goto fail_type_alloc_name; + new_defs[size].name = name; + } + } + + // append array terminator + new_defs = type_realloc(new_defs, size); + if (!new_defs) + goto fail_type_alloc; + new_defs[size].type = TRACEEVAL_TYPE_NONE; + + return new_defs; +fail_type_alloc: + if (defs[size].name) + print_err("failed to allocate memory for traceeval_type %s", defs[size].name); + print_err("failed to allocate memory for traceeval_type index %zu", size); + return NULL; + +fail_type_alloc_name: + if (defs[size].name) + print_err("failed to allocate name for traceeval_type %s", defs[size].name); + + print_err("failed to allocate name for traceeval_type index %zu", size); + return NULL; +} + +/** + * Create a new histogram over the given keys and values. + */ +struct traceeval *traceeval_init(const struct traceeval_type *keys, + const struct traceeval_type *vals) +{ + struct traceeval *teval; + char *err_msg; + struct traceeval_type type = { + .type = TRACEEVAL_TYPE_NONE + }; + + if (!keys) + return NULL; + + if (keys->type == TRACEEVAL_TYPE_NONE) { + err_msg = "keys cannot start with type TRACEEVAL_TYPE_NONE"; + goto fail_eval_init_unalloced; + } + + teval = calloc(1, sizeof(struct traceeval)); + if (!teval) { + err_msg = "failed to allocate memory for traceeval instance"; + goto fail_eval_init_unalloced; + } + + teval->def_keys = type_alloc(keys); + if (!teval->def_keys) { + err_msg = "failed to allocate user defined keys"; + goto fail_eval_init; + } + + // if vals is NULL, alloc single type NONE + if (vals) + teval->def_vals = type_alloc(vals); + else + teval->def_vals = type_alloc(&type); + + if (!teval->def_vals) { + err_msg = "failed to allocate user defined values"; + goto fail_eval_init; + } + + teval->hist = calloc(1, sizeof(struct hist_table)); + if (!teval->hist) { + err_msg = "failed to allocate memory for histogram"; + goto fail_eval_init; + } + teval->hist->nr_entries = 0; + + return teval; +fail_eval_init: + traceeval_release(teval); + /* fall-through */ + +fail_eval_init_unalloced: + print_err(err_msg); + return NULL; +} + +// TODO +void traceeval_release(struct traceeval *teval) +{ + +} + +// TODO +int traceeval_insert(struct traceeval *teval, + const union traceeval_data *keys, + const union traceeval_data *vals) +{ + return -1; +} + +// TODO +int traceeval_query(struct traceeval *teval, + const union traceeval_data *keys, + union traceeval_data * const *results) +{ + return 0; +} + +// TODO +int traceeval_find_key(struct traceeval *teval, const char *field) +{ + return -1; +} + +// TODO +int traceeval_find_val(struct traceeval *teval, const char *field) +{ + return -1; +} + +// TODO +void traceeval_results_release(struct traceeval *teval, + const union traceeval_data *results) +{ + +} + +// TODO +struct traceeval_iterator *traceeval_iterator_get(struct traceeval *teval) +{ + return NULL; +} + +// TODO +int traceeval_iterator_sort(struct traceeval_iterator *iter, + const char *sort_field, int level, bool ascending) +{ + return -1; +} + +// TODO +int traceeval_iterator_next(struct traceeval_iterator *iter, + const union traceeval_data **keys) +{ + return 0; +} + +// TODO +void traceeval_keys_release(struct traceeval_iterator *iter, + const union traceeval_data *keys) +{ + +} + +// TODO +int traceeval_stat(struct traceeval *teval, const union traceeval_data *keys, + const char *field, struct traceeval_stat *stat) +{ + return -1; +} From patchwork Fri Jul 28 19:04:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stevie Alvarez X-Patchwork-Id: 13332591 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E4CA7C0015E for ; Fri, 28 Jul 2023 19:05:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229509AbjG1TFp (ORCPT ); Fri, 28 Jul 2023 15:05:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36252 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229740AbjG1TFn (ORCPT ); Fri, 28 Jul 2023 15:05:43 -0400 Received: from mail-il1-x132.google.com (mail-il1-x132.google.com [IPv6:2607:f8b0:4864:20::132]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 33FEC3AB3 for ; Fri, 28 Jul 2023 12:05:42 -0700 (PDT) Received: by mail-il1-x132.google.com with SMTP id e9e14a558f8ab-348db491d0eso10588355ab.3 for ; Fri, 28 Jul 2023 12:05:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1690571141; x=1691175941; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Z1ssfZnnt5Fl/bPSmz3r2pmt8oVxKZGgBLxCrOoJiIc=; b=jX11NnuAJMphNL0qOdzPhAotZ7PIR1sQT6oPfMICBurSmTIAJCiehF9sehjtnxdpM+ rBLs3Y9hvOeN7BXlcGXG7mCpa5TaH25LJt1SzYkbDzmzFnW/W+octu8UsGaHys8V0oeP Mg/sdBtbZsHPy/kp3TxzXeihjYMjA5zkPWB+o8Nruf5Llhudff3uzOcD/jdIoh4zLHDg cZEe6Rp92leYbJIsUSxmWdYOHgOi1hEYa3VxPxG9vx37ci6Z5e9zf9QmgN3ES12MXOr7 VNmJlY87vqAXD08p04r7A4ZunwNdZErInFnc2t11e/ubrL33GWSzfHsIaJlK37FlEQoV elmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690571141; x=1691175941; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Z1ssfZnnt5Fl/bPSmz3r2pmt8oVxKZGgBLxCrOoJiIc=; b=Y5kAdPOas5BuUalMFn9k671DjLu+7Sob1be8zkxpBSrZq/GgZiSXN6XDJjxQPRxFIn t/U3OcMdKr4YDLFVxmBtP9Y4f9qQ8T5fdPYAp7p/iafN7hKOwt9Ul45/pfDT/P75epAJ DxeVrUwWZcJVcLmwMW7tpQX5db7VzrY0F/FA62WwgSSGMt7UIvPFgbPG1eFoqVFiLxcb XAHFEPcvEGKu9h3gFnqfyBai/lL03taNWmSytRjfkwleMO9q+Z2YPx1iUIc9FofA2tK1 oahYiGJTTEJK5FQnBzHOnHu9xo7J8HXzJSJADfCyv+2zYlUIXrMVCyzLW5vJ0DIalAWv tWWQ== X-Gm-Message-State: ABy/qLZwNo/V2PA9HVQKe82EhCoca6pjTOy8xV6iaeE6oDP87wBxI/5d ngnKi5XdahQTPYGd2+hAuotKHII2EuuFxg== X-Google-Smtp-Source: APBJJlFVPCATJoEPny6NMLYeOLLtZYdm8IqrJwBgnfeLa0NCHC39vxLtwZj38QKkAd7o6BXggPsacQ== X-Received: by 2002:a05:6602:692:b0:780:ce72:ac55 with SMTP id dp18-20020a056602069200b00780ce72ac55mr436293iob.10.1690571141236; Fri, 28 Jul 2023 12:05:41 -0700 (PDT) Received: from localhost.localdomain ([2a00:79e1:abc:1705:d317:f8ec:adf0:bb06]) by smtp.gmail.com with ESMTPSA id u22-20020a02aa96000000b0042b3a328ee0sm1268452jai.166.2023.07.28.12.05.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jul 2023 12:05:40 -0700 (PDT) From: Stevie Alvarez To: linux-trace-devel@vger.kernel.org Cc: "Stevie Alvarez (Google)" , Steven Rostedt , Ross Zwisler Subject: [PATCH 3/5] histograms: traceeval release Date: Fri, 28 Jul 2023 15:04:38 -0400 Message-ID: <20230728190515.23088-3-stevie.6strings@gmail.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230728190515.23088-1-stevie.6strings@gmail.com> References: <20230728190515.23088-1-stevie.6strings@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Stevie Alvarez (Google)" traceeval_release() deconstructs a given struct traceeval instance. It frees any data allocated to the heap within the union traceeval_data arrays of entries to the histogram, and the names allocated for the struct traceeval_type key-value definitions. Signed-off-by: Stevie Alvarez (Google) --- src/histograms.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/src/histograms.c b/src/histograms.c index 13830e4..f46a0e0 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -209,10 +209,93 @@ fail_eval_init_unalloced: return NULL; } -// TODO -void traceeval_release(struct traceeval *teval) +/** + * Deallocate array of traceeval_type's, which must be terminated by + * TRACEEVAL_TYPE_NONE. + */ +static void type_release(struct traceeval_type *defs) { + size_t i = 0; + + if (!defs) + return; + + for_each_key(i, defs) { + if (defs[i].name) + free(defs[i].name); + } + + free(defs); +} + +/** + * Deallocate any specified dynamic data in @data. + */ +static void clean_data(union traceeval_data *data, struct traceeval_type *def) +{ + size_t i = 0; + + if (!data || !def) + return; + + for_each_key(i, def) { + switch (def[i].type) { + case TRACEEVAL_TYPE_STRING: + if (data[i].string) + free(data[i].string); + break; + case TRACEEVAL_TYPE_DYNAMIC: + def[i].dyn_release(data[i].dyn_data, &def[i]); + break; + default: + break; + } + } +} +/** + * Deallocate all possible data stored within the entry. + */ +static void clean_entry(struct entry *entry, struct traceeval *teval) +{ + if (!entry) + return; + + // deallocate dynamic traceeval_data + clean_data(entry->keys, teval->def_keys); + clean_data(entry->vals, teval->def_vals); + free(entry->keys); + free(entry->vals); +} + +/** + * Deallocate the hist_table allocated to a traceeval instance. + */ +static void hist_table_release(struct traceeval *teval) +{ + struct hist_table *hist = teval->hist; + + if (!hist) + return; + + for (size_t i = 0; i < hist->nr_entries; i++) { + clean_entry(&hist->map[i], teval); + } + free(hist->map); + free(hist); +} + +/** + * Deallocate a traceeval instance. + */ +void traceeval_release(struct traceeval *teval) +{ + if (teval) { + hist_table_release(teval); + type_release(teval->def_keys); + type_release(teval->def_vals); + free(teval); + } } // TODO From patchwork Fri Jul 28 19:04:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stevie Alvarez X-Patchwork-Id: 13332593 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C7E2BC00528 for ; Fri, 28 Jul 2023 19:05:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229888AbjG1TFq (ORCPT ); Fri, 28 Jul 2023 15:05:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36264 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230125AbjG1TFo (ORCPT ); Fri, 28 Jul 2023 15:05:44 -0400 Received: from mail-il1-x12c.google.com (mail-il1-x12c.google.com [IPv6:2607:f8b0:4864:20::12c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9D3AF3AB9 for ; Fri, 28 Jul 2023 12:05:42 -0700 (PDT) Received: by mail-il1-x12c.google.com with SMTP id e9e14a558f8ab-348fac49ef1so6803545ab.0 for ; Fri, 28 Jul 2023 12:05:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1690571142; x=1691175942; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LdH3/AbrasPZEt7KrIB8Ff8OpN57OVIxmlHAZKL3EfU=; b=NLTfTjOOodC9edY/h2aNA512Dt5/ncs4IIMAJHopiAX7QnWqYVAn9lp6Vq7GUOi2zf Qfyat3J1+CpsnbKoFvhVK/7bGh5KgLHLrkoL3OEGOmUYRLJX5GDbSYSyW1jn261/NeDL hFtwQnD/jViuyb7twBZrrHAqJ+QcvZ31MswB0L9ZxxBEQJ3AhHcbbXYlOvahe/kagmnl gRUaEd2K+oOTizRFLOV5acsI80tKUhUmBj9a5ISGI4P3JPKxP4+YGbrnQ1g4mf27Q2l8 hNt5pUceiq/UILPFFD/bgsnxlBF4gzScL6iE626yyH4BpuVdcqXYhFKizDLRLKPputS+ OJ9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690571142; x=1691175942; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LdH3/AbrasPZEt7KrIB8Ff8OpN57OVIxmlHAZKL3EfU=; b=fLJc3agiJUNd0+JKFWTcF+paTLlNoIV0l7Dzlp7NRX91s614DGTApOzGEnGfId5kDz yCJEng8XyB2ISKjITXT+PNN0OCRidAdVg9PGqbLsQGI9QC6wB4GSMfZE8K6uaTltZOBK q0mM//ruu/B9dODN9Ev3TbvaOgeciraaVXBJ96BfXIa2A5cd4a2AmQXiV7/eNFaaqMK1 7M1azyXX2qCv6VmsgFf5XR/TaKwsSN7cOjrEJ7Ed1IYi0saJSr7Q3wR0xhnUa7EvQfIz ov1GUymGsvZgPFC3TYQTx/TkBLPFIhwdCm3HJA7btYKaANZ6Tdn9NXHxG4VBqDW22Mq2 A99A== X-Gm-Message-State: ABy/qLa7e9MSKq4NQ9aF0KDfswDmtiL1eddCj3jBD++LM+EM4hgTeOF4 ocKj6ylVQ6WdoXvBqc5AtKO4IAQdicJE7A== X-Google-Smtp-Source: APBJJlG9iFqxrH9dY3ri1pQcNdiBsWX8QnZAQDPKndS8MP44P7/IabGI21hlWIaa6Jmq5ZWuqMMuBg== X-Received: by 2002:a05:6e02:2142:b0:348:987a:fd8c with SMTP id d2-20020a056e02214200b00348987afd8cmr391169ilv.31.1690571141879; Fri, 28 Jul 2023 12:05:41 -0700 (PDT) Received: from localhost.localdomain ([2a00:79e1:abc:1705:d317:f8ec:adf0:bb06]) by smtp.gmail.com with ESMTPSA id u22-20020a02aa96000000b0042b3a328ee0sm1268452jai.166.2023.07.28.12.05.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jul 2023 12:05:41 -0700 (PDT) From: Stevie Alvarez To: linux-trace-devel@vger.kernel.org Cc: "Stevie Alvarez (Google)" , Steven Rostedt , Ross Zwisler Subject: [PATCH 4/5] histograms: traceeval compare Date: Fri, 28 Jul 2023 15:04:39 -0400 Message-ID: <20230728190515.23088-4-stevie.6strings@gmail.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230728190515.23088-1-stevie.6strings@gmail.com> References: <20230728190515.23088-1-stevie.6strings@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Stevie Alvarez (Google)" traceeval_compare() compares two struct traceeval instances for equality. This suite of comparitors was made for testing purposes. Signed-off-by: Stevie Alvarez (Google) --- src/histograms.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 2 deletions(-) diff --git a/src/histograms.c b/src/histograms.c index f46a0e0..5f1c7ef 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -20,6 +20,19 @@ #define for_each_key(i, keys) \ for (i = 0; (keys)[(i)].type != TRACEEVAL_TYPE_NONE; (i)++) +/** + * Compare two integers of variable length. + * + * Return 0 if @a and @b are the same, 1 if @a is greater than @b, and -1 + * if @b is greater than @a. + */ +#define compare_numbers_return(a, b) \ +do { \ + if ((a) < (b)) \ + return -1; \ + return (a) != (b); \ +} while (0) \ + /** A key-value pair */ struct entry { union traceeval_data *keys; @@ -56,10 +69,171 @@ static void print_err(const char *fmt, ...) fprintf(stderr, "\n"); } -// TODO +/** + * Return 0 if @orig and @copy are the same, 1 otherwise. + */ +static int compare_traceeval_type(struct traceeval_type *orig, + struct traceeval_type *copy) +{ + int o_name_null; + int c_name_null; + + // same memory/null + if (orig == copy) + return 0; + + size_t i = 0; + do { + if (orig[i].type != copy[i].type) + return 1; + if (orig[i].type == TRACEEVAL_TYPE_NONE) + return 0; + if (orig[i].flags != copy[i].flags) + return 1; + if (orig[i].id != copy[i].id) + return 1; + if (orig[i].dyn_release != copy[i].dyn_release) + return 1; + if (orig[i].dyn_cmp != copy[i].dyn_cmp) + return 1; + + // make sure both names are same type + o_name_null = !orig[i].name; + c_name_null = !copy[i].name; + if (o_name_null != c_name_null) + return 1; + if (o_name_null) + continue; + if (strcmp(orig[i].name, copy[i].name) != 0) + return 1; + } while (orig[i++].type != TRACEEVAL_TYPE_NONE); + + return 0; +} + +/** + * Return 0 if @orig and @copy are the same, 1 if @orig is greater than @copy, + * -1 for the other way around, and -2 on error. + */ +static int compare_traceeval_data(union traceeval_data *orig, + const union traceeval_data *copy, struct traceeval_type *type) +{ + if (!orig || !copy) + return 1; + + switch (type->type) { + case TRACEEVAL_TYPE_NONE: + /* There is no corresponding traceeval_data for TRACEEVAL_TYPE_NONE */ + return -2; + + case TRACEEVAL_TYPE_STRING: + int i = strcmp(orig->string, copy->string); + if (!i) + return 0; + if (i > 0) + return 1; + return -1; + + case TRACEEVAL_TYPE_NUMBER: + compare_numbers_return(orig->number, copy->number); + + case TRACEEVAL_TYPE_NUMBER_64: + compare_numbers_return(orig->number_64, copy->number_64); + + case TRACEEVAL_TYPE_NUMBER_32: + compare_numbers_return(orig->number_32, copy->number_32); + + case TRACEEVAL_TYPE_NUMBER_16: + compare_numbers_return(orig->number_16, copy->number_16); + + case TRACEEVAL_TYPE_NUMBER_8: + compare_numbers_return(orig->number_8, copy->number_8); + + case TRACEEVAL_TYPE_DYNAMIC: + return type->dyn_cmp(orig->dyn_data, copy->dyn_data, type); + + default: + print_err("%d is out of range of enum traceeval_data_type", type->type); + return -2; + } +} + +/** + * Compare arrays fo union traceeval_data's with respect to @def. + * + * Return 0 if @orig and @copy are the same, 1 if not, and -1 on error. + */ +static int compare_traceeval_data_set(union traceeval_data *orig, + const union traceeval_data *copy, struct traceeval_type *def) +{ + int i = 0; + int check; + + // compare data arrays + for_each_key(i, def) { + if ((check = compare_traceeval_data(&orig[i], ©[i], &def[i]))) + goto fail_compare_data_set; + } + + return 0; +fail_compare_data_set: + if (check == -2) + return -1; + return 1; +} + +/** + * Return 0 if @orig and @copy are the same, 1 if not, and -1 on error. + */ +static int compare_entries(struct entry *orig, struct entry *copy, + struct traceeval *teval) +{ + int check; + + // compare keys + check = compare_traceeval_data_set(orig->keys, copy->keys, + teval->def_keys); + if (check) + return check; + + // compare values + check = compare_traceeval_data_set(orig->vals, copy->vals, + teval->def_vals); + return check; +} + +/** + * Return 0 if struct hist_table of @orig and @copy are the same, 1 if not, + * and -1 on error. + */ +static int compare_hist(struct traceeval *orig, struct traceeval *copy) +{ + struct hist_table *o_hist = orig->hist; + struct hist_table *c_hist = copy->hist; + int cnt = !(o_hist->nr_entries == c_hist->nr_entries); + if (cnt) + return 1; + + for (size_t i = 0; i < o_hist->nr_entries; i++) { + // cmp each entry + compare_entries(&o_hist->map[i], &c_hist->map[i], orig); + } + return 0; +} + +/** + * Return 0 if @orig and @copy are the same, 1 if not, -1 if error. + */ int traceeval_compare(struct traceeval *orig, struct traceeval *copy) { - return -1; + if ((!orig) || (!copy)) + return -1; + + int keys = compare_traceeval_type(orig->def_keys, copy->def_keys); + int vals = compare_traceeval_type(orig->def_vals, copy->def_vals); + int hists = compare_hist(orig, copy); + + return (keys || vals || hists); } /** From patchwork Fri Jul 28 19:04:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stevie Alvarez X-Patchwork-Id: 13332594 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0F3B1C41513 for ; Fri, 28 Jul 2023 19:05:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230125AbjG1TFr (ORCPT ); Fri, 28 Jul 2023 15:05:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36262 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230259AbjG1TFp (ORCPT ); Fri, 28 Jul 2023 15:05:45 -0400 Received: from mail-il1-x136.google.com (mail-il1-x136.google.com [IPv6:2607:f8b0:4864:20::136]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B910FE48 for ; Fri, 28 Jul 2023 12:05:43 -0700 (PDT) Received: by mail-il1-x136.google.com with SMTP id e9e14a558f8ab-348f5193c12so8521165ab.2 for ; Fri, 28 Jul 2023 12:05:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1690571143; x=1691175943; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=mp7/cVbHihUV4P3uaG03ycADHtmZqZ64FVpuX6pQwA0=; b=mOnjU8vfrO9PrxwoiQpjJivpteVw8rnUeZNvw1qHtwaD24jOJAK3cS7VVq+RFm3e7t H8Pc6/B0QtvsCdM8YnN5HfG21LndhTjBT15UPkJ0ywZtL18CzPwr2vBeb/LryQ3ExCoH 676Ig+mOuYa8UXRnWz3dqmMV1siR2w4aT+T1eTRp9ikgWWE5uPAkh8EssOpCFYR7CTQu 0zwy1I6y+LyCpTrE5lFhTBej4OhVpvO9AF8FXRmuvbcAwNGhar0tov7telLArOTB8TvV W7kApcEjWs2jGZ2p1FgjQjU/6KQAsGDSyOVtW1F4JQDgyGW5AQMjT5Zy2jDtVczMbrtG FSfQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690571143; x=1691175943; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mp7/cVbHihUV4P3uaG03ycADHtmZqZ64FVpuX6pQwA0=; b=BKnaOOp/xKQvkXWk1JxjUKPpOtGb9gp2QqriV6CovGJW5ESqDgd7JETtXwf8FucwcU AwKk8+whZdAVAn9jjOaFsumbnGv+2PQ4sEONDtnn6+C81ulkxlTP6l3SR/PoykP8Y6vN GJdz1QJ7U9LNYTlJnrBXWonKEWkrnRgBbrJ7z6c0CbEhWbHa1amKQVE97UhqBUy2xiA2 F7p9nUqvmiWIQqeiD/K24etK7Qc8LSLPizIy+kG66LnGoIx8CBrUkxwDv+ZiGXjZx/Pi i0zdN2MtMxC1xv9mdBQy7IU3EZGHr4l43d7V0p4lLZQfNvdhSyO7l5vgCYaI2MTBBEMV P0hg== X-Gm-Message-State: ABy/qLYoWBOdyGa/lweX0wsQomfSo22L0Q+btG9r3leiGuCWX3EwqSt+ KKX+RmvCe5oquj0fxdy0smLc/NsTgbxweg== X-Google-Smtp-Source: APBJJlG6LZrn88YKcL0XoaU24kfFfBaG1JMBcvpz+lt/WOqt/RK4rc7nIrjfIX+UbiTS0mC385GSIA== X-Received: by 2002:a05:6e02:1a6d:b0:348:c940:1845 with SMTP id w13-20020a056e021a6d00b00348c9401845mr541515ilv.8.1690571142724; Fri, 28 Jul 2023 12:05:42 -0700 (PDT) Received: from localhost.localdomain ([2a00:79e1:abc:1705:d317:f8ec:adf0:bb06]) by smtp.gmail.com with ESMTPSA id u22-20020a02aa96000000b0042b3a328ee0sm1268452jai.166.2023.07.28.12.05.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jul 2023 12:05:42 -0700 (PDT) From: Stevie Alvarez To: linux-trace-devel@vger.kernel.org Cc: "Stevie Alvarez (Google)" , Steven Rostedt , Ross Zwisler Subject: [PATCH 5/5] histograms: Add struct traceeval unit tests Date: Fri, 28 Jul 2023 15:04:40 -0400 Message-ID: <20230728190515.23088-5-stevie.6strings@gmail.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230728190515.23088-1-stevie.6strings@gmail.com> References: <20230728190515.23088-1-stevie.6strings@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Stevie Alvarez (Google)" Add unit tests for traceeval_init(), traceeval_release(), and traceeval_compare() to check edge cases and ensure the interface is functional. Signed-off-by: Stevie Alvarez (Google) --- utest/.gitignore | 1 + utest/Makefile | 35 +++++++ utest/eval-test.h | 10 ++ utest/eval-utest.c | 27 +++++ utest/traceeval-utest.c | 222 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 utest/.gitignore create mode 100644 utest/Makefile create mode 100644 utest/eval-test.h create mode 100644 utest/eval-utest.c create mode 100644 utest/traceeval-utest.c diff --git a/utest/.gitignore b/utest/.gitignore new file mode 100644 index 0000000..ca0ac10 --- /dev/null +++ b/utest/.gitignore @@ -0,0 +1 @@ +eval-utest diff --git a/utest/Makefile b/utest/Makefile new file mode 100644 index 0000000..955f91e --- /dev/null +++ b/utest/Makefile @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: MIT + +include $(src)/scripts/utils.mk + +bdir := $(obj)/utest +eval_lib := $(obj)/lib/libtraceeval.a + +TARGETS = $(bdir)/eval-utest + +OBJS := eval-utest.o +OBJS += traceeval-utest.o + +LIBS += -lcunit \ + -ldl \ + $(eval_lib) + +OBJS := $(OBJS:%.o=$(bdir)/%.o) + +$(bdir): + @mkdir -p $(bdir) + +$(OBJS): | $(bdir) + +$(bdir)/eval-utest: $(OBJS) $(eval_lib) + $(Q)$(do_app_build) + +$(bdir)/%.o: %.c + $(Q)$(call do_fpic_compile) + +-include .*.d + +test: $(TARGETS) + +clean: + $(Q)$(call do_clean,$(TARGETS) $(bdir)/*.o $(bdir)/.*.d) diff --git a/utest/eval-test.h b/utest/eval-test.h new file mode 100644 index 0000000..f3372e8 --- /dev/null +++ b/utest/eval-test.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (C) 2023 Google Inc, Stevie Alvarez + */ +#ifndef _EVAL_UTEST_H_ +#define _EVAL_UTEST_H_ + +void test_traceeval_lib(void); + +#endif /* _EVAL_UTEST_H_ */ diff --git a/utest/eval-utest.c b/utest/eval-utest.c new file mode 100644 index 0000000..771a0c4 --- /dev/null +++ b/utest/eval-utest.c @@ -0,0 +1,27 @@ + +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2023 Google Inc, Stevie Alvarez + */ +#include +#include + +#include +#include + +#include "eval-test.h" + +int main(int argc, char **argv) +{ + if (CU_initialize_registry() != CUE_SUCCESS) { + printf("Test registry cannot be initialized\n"); + return EXIT_FAILURE; + } + + test_traceeval_lib(); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_cleanup_registry(); + return EXIT_SUCCESS; +} diff --git a/utest/traceeval-utest.c b/utest/traceeval-utest.c new file mode 100644 index 0000000..2bcb4b9 --- /dev/null +++ b/utest/traceeval-utest.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2023 Google Inc, Stevie Alvarez + */ + +#include +#include +#include +#include +#include + +#include + +#define TRACEEVAL_SUITE "traceeval library" +#define TRACEEVAL_SUCCESS 0 +#define TRACEEVAL_FAILURE -1 +#define TRACEEVAL_NOT_SAME 1 + +/** + * Test traceeval_init(), traceeval_release(), and traceeval_compare() with + * NULL values. + */ +void test_eval_null(void) +{ + // set up + char *name = "test null"; + enum traceeval_data_type type = TRACEEVAL_TYPE_NONE; + const struct traceeval_type test_data[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = name + }, + { + .type = type, + .name = NULL + } + }; + + // test init + struct traceeval *result_null = traceeval_init(NULL, NULL); + struct traceeval *result_key = traceeval_init(test_data, NULL); + struct traceeval *result_val = traceeval_init(NULL, test_data); + + // analyze init + CU_ASSERT(!result_null); + CU_ASSERT(result_key != NULL); + CU_ASSERT(!result_val); + + // release + traceeval_release(NULL); + traceeval_release(result_key); +} + +/** + * Use provided data to test traceeval_init(), traceeval_compare(), and + * traceeval_release(). + */ +void test_eval_base(const struct traceeval_type *keys1, + const struct traceeval_type *vals1, + const struct traceeval_type *keys2, + const struct traceeval_type *vals2, + bool init_not_null1, bool init_not_null2, + int compare_result) +{ + struct traceeval *init1; + struct traceeval *init2; + int result; + + // test init + init1 = traceeval_init(keys1, vals1); + init2 = traceeval_init(keys2, vals2); + + result = init1 != NULL; + if (init_not_null1) { + CU_ASSERT(result); + } else { + CU_ASSERT(!result); + } + + result = init2 != NULL; + if (init_not_null2) { + CU_ASSERT(result); + } else { + CU_ASSERT(!result); + } + + // test compare + result = traceeval_compare(init1, init2); + + // analyze compare + CU_ASSERT(result == compare_result); + + // release + traceeval_release(init1); + traceeval_release(init2); +} + +/** + * Test traceeval_init(), traceeval_release(), and traceeval_compare() with + * TRACEEVAL_TYPE_NONE. + */ +void test_eval_none(void) +{ + // set up + char *name = "test none"; + char *name2 = "test none (some)"; + const struct traceeval_type test_data_none[] = { + { + .type = TRACEEVAL_TYPE_NONE, + .name = name + } + }; + const struct traceeval_type test_data_some[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = name2 + }, + { + .type = TRACEEVAL_TYPE_NONE, + .name = NULL + } + }; + + test_eval_base(test_data_some, test_data_none, test_data_some, + test_data_none, true, true, TRACEEVAL_SUCCESS); + test_eval_base(test_data_none, test_data_none, test_data_some, + test_data_none, false, true, TRACEEVAL_FAILURE); + test_eval_base(test_data_none, test_data_none, test_data_none, + test_data_none, false, false, TRACEEVAL_FAILURE); +} + +/** + * Test traceeval_init() and traceeval_release() with equivalent values. + */ +void test_eval_same(void) +{ + // set up + char *name = "test data"; + char *name2 = "test done"; + const struct traceeval_type test_data[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = name + }, + { + .type = TRACEEVAL_TYPE_NONE, + .name = name2 + } + }; + + test_eval_base(test_data, test_data, test_data, test_data, true, true, + TRACEEVAL_SUCCESS); +} + +/** + * Test traceeval_init() and traceeval_release() with non-equivalent values. + */ +void test_eval_not_same(void) +{ + const struct traceeval_type test_data1[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "test data 1" + }, + { + .type = TRACEEVAL_TYPE_NONE, + .name = NULL + } + }; + const struct traceeval_type test_data2[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "test data 2" + }, + { + .type = TRACEEVAL_TYPE_NONE, + .name = NULL + } + }; + + // type 1 key diff + test_eval_base(test_data2, test_data1, test_data1, test_data1, true, + true, TRACEEVAL_NOT_SAME); + // type 1 data diff + test_eval_base(test_data1, test_data2, test_data1, test_data1, true, + true, TRACEEVAL_NOT_SAME); + // type 2 key diff + test_eval_base(test_data1, test_data1, test_data2, test_data1, true, + true, TRACEEVAL_NOT_SAME); + // type 2 data diff + test_eval_base(test_data1, test_data1, test_data1, test_data2, true, + true, TRACEEVAL_NOT_SAME); +} + +/** + * Tests the traceeval_init() and traceeval_release() functions. + */ +void test_eval(void) +{ + test_eval_null(); + test_eval_none(); + test_eval_same(); + test_eval_not_same(); +} + +/** + * Register tests with CUnit. + */ +void test_traceeval_lib(void) +{ + CU_pSuite suite = NULL; + + // set up suite + suite = CU_add_suite(TRACEEVAL_SUITE, NULL, NULL); + if (suite == NULL) { + fprintf(stderr, "Suite %s cannot be created\n", TRACEEVAL_SUITE); + return; + } + + // add tests to suite + CU_add_test(suite, "Test traceeval alloc and release", test_eval); +}