1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2019, Google and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 * 24 */ 25 26 #include "precompiled.hpp" 27 #include "classfile/tsanIgnoreList.hpp" 28 #include "classfile/symbolTable.hpp" 29 #include "memory/resourceArea.inline.hpp" 30 31 static const int MAX_LINE_SIZE = 1024; 32 33 class FieldMatcher : public CHeapObj<mtClass> { 34 public: 35 enum Mode { 36 Exact = 0, 37 Prefix = 1, 38 Any = 2, 39 Unknown = -1 40 }; 41 42 FieldMatcher(const Symbol* class_name, Mode class_mode, 43 const Symbol* field_name, Mode field_mode, FieldMatcher* next) 44 : _class_name(class_name), 45 _field_name(field_name), 46 _class_mode(class_mode), 47 _field_mode(field_mode), 48 _next(next) { } 49 50 // Given a FieldMatcher as the head of linked-list, returns true if any 51 // FieldMatcher in the list matches. 52 static bool match_any(FieldMatcher* head, 53 const Symbol* class_name, 54 const Symbol* field_name) { 55 while (head) { 56 if (head->match(class_name, field_name)) { 57 return true; 58 } 59 head = head->_next; 60 } 61 return false; 62 } 63 64 protected: 65 const Symbol* _class_name; 66 const Symbol* _field_name; 67 Mode _class_mode; 68 Mode _field_mode; 69 FieldMatcher* _next; 70 71 static bool match(const Symbol* candidate, const Symbol* match, Mode mode) { 72 ResourceMark rm; 73 switch (mode) { 74 case Exact: 75 return candidate == match; 76 case Prefix: { 77 const char* candidate_str = candidate->as_C_string(); 78 const char* match_str = match->as_C_string(); 79 return (strstr(candidate_str, match_str) == candidate_str); 80 } 81 case Any: 82 return true; 83 default: 84 return false; 85 } 86 } 87 88 bool match(const Symbol* class_name, const Symbol* field_name) { 89 return (match(class_name, _class_name, _class_mode) && 90 match(field_name, _field_name, _field_mode)); 91 } 92 }; 93 94 FieldMatcher* TsanIgnoreList::_exact_match = NULL; 95 FieldMatcher* TsanIgnoreList::_prefix_match = NULL; 96 97 // Detects the pattern-matching mode based on the presence and location of 98 // wildcard character, fixes the pattern inplace and returns the 99 // pattern-matching mode. 100 static FieldMatcher::Mode make_pattern(char* pattern) { 101 const int len = strlen(pattern); 102 // Inverse of Symbol::as_klass_external_name. 103 // Turn all '.'s into '/'s. 104 for (int index = 0; index < len; index++) { 105 if (pattern[index] == '.') { 106 pattern[index] = '/'; 107 } 108 } 109 110 char* asterisk = strstr(pattern, "*"); 111 if (asterisk == NULL) { 112 return FieldMatcher::Exact; 113 } 114 if (asterisk - pattern != len - 1) { 115 warning("Unexpected location for '*' in \"%s\". " 116 "Only prefix patterns are supported.", pattern); 117 } 118 if (asterisk == pattern) { 119 return FieldMatcher::Any; 120 } 121 pattern[len - 1] = '\0'; 122 return FieldMatcher::Prefix; 123 } 124 125 void TsanIgnoreList::parse_from_line(char* line) { 126 EXCEPTION_MARK; 127 char class_pattern[MAX_LINE_SIZE], field_pattern[MAX_LINE_SIZE]; 128 // Replace '#' with '\0'. 129 { 130 char* comment = strchr(line, '#'); 131 if (comment != NULL) { 132 *comment = '\0'; 133 } 134 } 135 // Parse line. 136 if (sscanf(line, "%s %s", class_pattern, field_pattern) != 2) { 137 return; 138 } 139 // Get matcher mode from pattern. 140 FieldMatcher::Mode class_mode = make_pattern(class_pattern); 141 FieldMatcher::Mode field_mode = make_pattern(field_pattern); 142 // If we match against Any, no need for a symbol, else create the symbol. 143 Symbol* class_symbol = (class_mode == FieldMatcher::Any) ? NULL : 144 SymbolTable::new_symbol(class_pattern); 145 Symbol* field_symbol = (field_mode == FieldMatcher::Any) ? NULL : 146 SymbolTable::new_symbol(field_pattern); 147 // Add matcher to beginning of linked list. 148 if (class_mode == FieldMatcher::Exact && field_mode == FieldMatcher::Exact) { 149 _exact_match = new FieldMatcher(class_symbol, class_mode, field_symbol, 150 field_mode, _exact_match); 151 } else { 152 _prefix_match = new FieldMatcher(class_symbol, class_mode, field_symbol, 153 field_mode, _prefix_match); 154 } 155 } 156 157 void TsanIgnoreList::parse_from_file(FILE* stream) { 158 char line[MAX_LINE_SIZE]; 159 while (fgets(line, sizeof(line), stream)) { 160 if (strlen(line) == sizeof(line) - 1) { 161 warning("TSAN ignore file (ThreadSanitizerIgnoreFile) contains a line longer " 162 "than %d. This pattern will be truncated, and the rest of the " 163 "file will not be processed for pattern matching.", 164 MAX_LINE_SIZE); 165 break; 166 } 167 parse_from_line(line); 168 } 169 if (ferror(stream)) { 170 warning("Error reading from TSAN ignore file"); 171 } 172 } 173 174 void TsanIgnoreList::init() { 175 if (ThreadSanitizerIgnoreFile == NULL) { 176 return; 177 } 178 179 FILE* stream = fopen(ThreadSanitizerIgnoreFile, "rt"); 180 if (stream == NULL) { 181 warning("TSAN ignore file (ThreadSanitizerIgnoreFile:%s) not found.", 182 ThreadSanitizerIgnoreFile); 183 return; 184 } 185 parse_from_file(stream); 186 fclose(stream); 187 } 188 189 bool TsanIgnoreList::match( 190 const Symbol* class_name, const Symbol* field_name, 191 BasicType type) { 192 // Wildcard matches are only for primitive types. References should be 193 // added to list individually since they become release/acquire. 194 if (is_java_primitive(type) && 195 FieldMatcher::match_any(_prefix_match, class_name, field_name)) { 196 return true; 197 } 198 return FieldMatcher::match_any(_exact_match, class_name, field_name); 199 }