/* Copyright (C) 2021-2024 Free Software Foundation, Inc. Contributed by Oracle. This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include #include #include #include #include "Filter.h" #include "util.h" #include "i18n.h" #include "data_pckts.h" #include "StringBuilder.h" #include "Experiment.h" // ======================================================================== // Subclass: FilterNumeric // Public Methods FilterNumeric::FilterNumeric (Experiment *_exp, const char *_cmd, const char *_name) { exp = _exp; cmd = dbe_strdup (_cmd); name = dbe_strdup (_name); pattern = NULL; status = NULL; items = NULL; prop_name = NULL; first = (uint64_t) - 1; last = (uint64_t) - 1; nselected = 0; nitems = 0; } FilterNumeric::~FilterNumeric () { free (cmd); free (name); free (pattern); free (status); Destroy (items); } // sets min and max for this filter; should be called when the range is // known -- that comes after the first PathTree build, in the current // sequence of things void FilterNumeric::set_range (uint64_t findex, uint64_t lindex, uint64_t total) { if (first == findex && last == lindex) return; first = findex; last = lindex; nitems = total; nselected = nitems; if (pattern) { free (pattern); pattern = NULL; } if (status) { free (status); status = NULL; } } void FilterNumeric::update_range () { if (exp == NULL) return; if (streq (cmd, NTXT ("sample"))) set_range (1, (uint64_t) exp->nsamples (), exp->nsamples ()); else if (streq (cmd, NTXT ("thread"))) set_range (exp->min_thread, exp->max_thread, exp->thread_cnt); else if (streq (cmd, NTXT ("LWP"))) set_range (exp->min_lwp, exp->max_lwp, exp->lwp_cnt); else if (streq (cmd, NTXT ("cpu"))) { if (exp->min_cpu != (uint64_t) - 1) set_range (exp->min_cpu, exp->max_cpu, exp->cpu_cnt); } } // get_advanced_filter -- returns a string matching the current setting char * FilterNumeric::get_advanced_filter () { if (items == NULL) return NULL; if (items->size () == 0) return dbe_strdup (NTXT ("0")); StringBuilder sb; if (items->size () > 1) sb.append ('('); for (int i = 0; i < items->size (); i++) { RangePair *rp = items->fetch (i); if (i > 0) sb.append (NTXT (" || ")); sb.append ('('); sb.append (prop_name); if (rp->first == rp->last) { sb.append (NTXT ("==")); sb.append ((long long) rp->first); } else { sb.append (NTXT (">=")); sb.append ((long long) rp->first); sb.append (NTXT (" && ")); sb.append (prop_name); sb.append (NTXT ("<=")); sb.append ((long long) rp->last); } sb.append (')'); } if (items->size () > 1) sb.append (')'); return sb.toString (); } // get_pattern -- returns a string matching the current setting char * FilterNumeric::get_pattern () { update_range (); if (pattern) return pattern; StringBuilder sb; if (items == NULL) { if (last == (uint64_t) - 1 && last == first) // neither set; data not available sb.append (GTXT ("(data not recorded)")); else sb.append (GTXT ("all")); } else if (items->size () == 0) sb.append (GTXT ("none")); else { for (int i = 0; i < items->size (); i++) { RangePair *rp = items->fetch (i); if (i > 0) sb.append (','); sb.append ((long long) rp->first); if (rp->first != rp->last) { sb.append ('-'); sb.append ((long long) rp->last); } } } pattern = sb.toString (); return pattern; } char * FilterNumeric::get_status () { update_range (); if (status == NULL) update_status (); return dbe_strdup (status); } // set_pattern -- set the filter to a new pattern // set error true/false if there was or was not an error parsing string // Returns true/false if the filter changed, implying a rebuild of data bool FilterNumeric::set_pattern (char *str, bool *error) { update_range (); // save the old filter Vector *olditems = items; *error = false; if (strcmp (str, NTXT ("all")) == 0) // if all, leave items NULL items = NULL; else if (strcmp (str, NTXT ("none")) == 0) // if none, leave items as a zero-length vector items = new Vector(0); else { uint64_t val, val2; char *s = str; char *nexts = s; items = NULL; for (bool done = false; done == false;) { // tokenize the string // Does it start with a "-" ? if (*nexts == '-') val = first; // yes, set val to first, and see what follows else { // it must start with a number val = get_next_number (s, &nexts, error); if (*error == true) break; } // look at the next character switch (*nexts) { case ',': s = ++nexts; *error = include_range (val, val); if (*error == true) done = true; break; case '-': s = ++nexts; if (*nexts == ',' || *nexts == '\0') val2 = last; else { val2 = get_next_number (s, &nexts, error); if (*error == true) { done = true; break; } } if (val > val2) { *error = true; done = true; break; } *error = include_range (val, val2); if (*error == true) { done = true; break; } if (*nexts == ',') { s = ++nexts; break; } if (*nexts == '\0') { done = true; break; } break; case '\0': *error = include_range (val, val); done = true; break; default: *error = true; done = true; break; } } // if there was a parser error leave old setting if (*error == true) { if (items) { items->destroy (); delete items; } items = olditems; return false; } } if (first != (uint64_t) - 1 && last != (uint64_t) - 1) { for (long i = VecSize (items) - 1; i >= 0; i--) { RangePair *rp = items->get (i); if ((rp->first > last) || (rp->last < first)) { delete rp; items->remove (i); continue; } if (rp->first < first) rp->first = first; if (rp->last > last) rp->last = last; } if (VecSize (items) == 1) { RangePair *rp = items->get (0); if ((rp->first == first) && (rp->last == last)) { // All, leave items NULL items->destroy (); delete items; items = NULL; } } } // no error, delete the old setting if (olditems != NULL) { olditems->destroy (); delete olditems; } bool changed; // regenerate the pattern if (pattern == NULL) changed = true; else { char *oldpattern = pattern; pattern = NULL; // to force a recompute with new values (void) get_pattern (); changed = strcmp (pattern, oldpattern) != 0; free (oldpattern); } return changed; } //================================================================ // Protected methods // set_status -- regenerate the status line, describing the current setting void FilterNumeric::update_status () { // regenerate the status line free (status); nselected = 0; if (items == NULL) { if (last == (uint64_t) - 1 && last == first) // neither set; data not available status = dbe_sprintf (GTXT ("(data not recorded)")); else if (first == (uint64_t) - 1 || last == (uint64_t) - 1) // range was not set status = dbe_sprintf (GTXT ("(all)")); else // range was set, compute percentage status = dbe_sprintf (GTXT ("total %lld, range: %lld-%lld"), (long long) nitems, (long long) first, (long long) last); } else { // some are selected int index; RangePair *rp; Vec_loop (RangePair *, items, index, rp) { nselected += rp->last - rp->first + 1; } if (last == (uint64_t) - 1) // range was not set status = dbe_sprintf (GTXT ("(%lld items selected)"), (long long) nselected); else // range was set status = dbe_sprintf (GTXT ("total %lld, range: %lld-%lld"), (long long) nitems, (long long) first, (long long) last); } } // Add a range to the filter; called from set_pattern for each index, // or index pair bool FilterNumeric::include_range (uint64_t findex, uint64_t lindex) { int index; RangePair *rp; if (findex > lindex) return true; bool done = false; if (items == NULL) items = new Vector(0); Vec_loop (RangePair *, items, index, rp) { if (findex < rp->first) { // Case where the new pair starts before the old if (lindex + 1 < rp->first) { // this pair comes cleanly in front of the current item RangePair *rp2 = new RangePair (); rp2->first = findex; rp2->last = lindex; items->insert (index, rp2); done = true; break; } // This new one extends the previous from the front rp->first = findex; chkextend: if (lindex <= rp->last) { // but does not extend the back done = true; break; } // extend this one out rp->last = lindex; // does it go into the next range? if (index == items->size () - 1) { // this is the last range, so it does not done = true; break; } RangePair *next = items->fetch (index + 1); if (lindex + 1 < next->first) { // no extension, we're done done = true; break; } // it does extend the next one next->first = rp->first; rp = next; // remove the current one, promoting next items->remove (index); goto chkextend; } else if (findex > rp->last + 1) // the new one is completely beyond the current continue; else { // the new one may start at or after the current, but it // extends it out; set the current // this pair overlaps the current item // rp-> first is OK -- it's equal or less than findex goto chkextend; } } if (done != true) { // fall through -- append to list rp = new RangePair (); rp->first = findex; rp->last = lindex; items->append (rp); } return false; } // Scan the filter to see if the number given is filtered in or out // return true if number is in, false if it's out bool FilterNumeric::is_selected (uint64_t number) { int index; RangePair *rp; if (items == NULL) return true; if (items->size () == 0) return false; Vec_loop (RangePair *, items, index, rp) { if (number >= rp->first && number <= rp->last) return true; } return false; } // get_next_number // Called from parser to extract a number from the current string position // Sets fail true if there was an error, false otherwise // returns the number as parsed uint64_t FilterNumeric::get_next_number (char *s, char **e, bool *fail) { errno = 0; *fail = false; uint64_t val = strtoll (s, e, 10); if (errno == EINVAL) *fail = true; return (val); }