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 }