1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 #include "precompiled.hpp"
  25 #include "logging/logTagLevelExpression.hpp"
  26 #include "logging/logTagSet.hpp"
  27 #include "runtime/arguments.hpp"
  28 #include "runtime/os.inline.hpp"
  29 
  30 const char* LogTagLevelExpression::DefaultExpressionString = "all";
  31 
  32 LogTagLevelExpression::~LogTagLevelExpression() {
  33   os::free(_string);
  34 }
  35 
  36 void LogTagLevelExpression::clear() {
  37   _ntags = 0;
  38   _ncombinations = 0;
  39   for (size_t combination = 0; combination < MaxCombinations; combination++) {
  40     _level[combination] = LogLevel::Invalid;
  41     _allow_other_tags[combination] = false;
  42     for (size_t tag = 0; tag < LogTag::MaxTags; tag++) {
  43       _tags[combination][tag] = LogTag::__NO_TAG;
  44     }
  45   }
  46   os::free(_string);
  47   _string = NULL;
  48 }
  49 
  50 bool LogTagLevelExpression::parse(const char* str, outputStream* errstream) {
  51   bool success = true;
  52   clear();
  53   if (str == NULL || strcmp(str, "") == 0) {
  54     str = DefaultExpressionString;
  55   }
  56   char* copy = os::strdup_check_oom(str, mtLogging);
  57   // Split string on commas
  58   for (char *comma_pos = copy, *cur = copy; success && comma_pos != NULL; cur = comma_pos + 1) {
  59     if (_ncombinations == MaxCombinations) {
  60       if (errstream != NULL) {
  61         errstream->print_cr("Can not have more than " SIZE_FORMAT " tag combinations in a what-expression.",
  62                             MaxCombinations);
  63       }
  64       success = false;
  65       break;
  66     }
  67 
  68     comma_pos = strchr(cur, ',');
  69     if (comma_pos != NULL) {
  70       *comma_pos = '\0';
  71     }
  72 
  73     // Parse the level, if specified
  74     LogLevelType level = LogLevel::Unspecified;
  75     char* equals = strchr(cur, '=');
  76     if (equals != NULL) {
  77       level = LogLevel::from_string(equals + 1);
  78       if (level == LogLevel::Invalid) {
  79         if (errstream != NULL) {
  80           errstream->print_cr("Invalid level '%s' in what-expression.", equals + 1);
  81         }
  82         success = false;
  83         break;
  84       }
  85       *equals = '\0'; // now ignore "=level" part of substr
  86     }
  87     set_level(level);
  88 
  89     // Parse special tags such as 'all'
  90     if (strcmp(cur, "all") == 0) {
  91       set_allow_other_tags();
  92       new_combination();
  93       continue;
  94     }
  95 
  96     // Check for '*' suffix
  97     char* asterisk_pos = strchr(cur, '*');
  98     if (asterisk_pos != NULL && asterisk_pos[1] == '\0') {
  99       set_allow_other_tags();
 100       *asterisk_pos = '\0';
 101     }
 102 
 103     // Parse the tag expression (t1+t2+...+tn)
 104     char* plus_pos;
 105     char* cur_tag = cur;
 106     do {
 107       plus_pos = strchr(cur_tag, '+');
 108       if (plus_pos != NULL) {
 109         *plus_pos = '\0';
 110       }
 111       LogTagType tag = LogTag::from_string(cur_tag);
 112       if (tag == LogTag::__NO_TAG) {
 113         if (errstream != NULL) {
 114           errstream->print_cr("Invalid tag '%s' in what-expression.", cur_tag);
 115         }
 116         success = false;
 117         break;
 118       }
 119       if (_ntags == LogTag::MaxTags) {
 120         if (errstream != NULL) {
 121           errstream->print_cr("Tag combination exceeds the maximum of " SIZE_FORMAT " tags.",
 122                               LogTag::MaxTags);
 123         }
 124         success = false;
 125         break;
 126       }
 127       add_tag(tag);
 128       cur_tag = plus_pos + 1;
 129     } while (plus_pos != NULL);
 130 
 131     new_combination();
 132   }
 133 
 134   // Save the (unmodified) string for printing purposes.
 135   _string = copy;
 136   strcpy(_string, str);
 137 
 138   return success;
 139 }
 140 
 141 LogLevelType LogTagLevelExpression::level_for(const LogTagSet& ts) const {
 142   LogLevelType level = LogLevel::Off;
 143   for (size_t combination = 0; combination < _ncombinations; combination++) {
 144     bool contains_all = true;
 145     size_t tag_idx;
 146     for (tag_idx = 0; tag_idx < LogTag::MaxTags && _tags[combination][tag_idx] != LogTag::__NO_TAG; tag_idx++) {
 147       if (!ts.contains(_tags[combination][tag_idx])) {
 148         contains_all = false;
 149         break;
 150       }
 151     }
 152     // All tags in the expression must be part of the tagset,
 153     // and either the expression allows other tags (has a wildcard),
 154     // or the number of tags in the expression and tagset must match.
 155     if (contains_all && (_allow_other_tags[combination] || tag_idx == ts.ntags())) {
 156       level = _level[combination];
 157     }
 158   }
 159   return level;
 160 }