Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2009-2015 Joel Rosdahl
3 : : *
4 : : * This program is free software; you can redistribute it and/or modify it
5 : : * under the terms of the GNU General Public License as published by the Free
6 : : * Software Foundation; either version 3 of the License, or (at your option)
7 : : * any later version.
8 : : *
9 : : * This program 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 for
12 : : * more details.
13 : : *
14 : : * You should have received a copy of the GNU General Public License along with
15 : : * this program; if not, write to the Free Software Foundation, Inc., 51
16 : : * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 : : */
18 : :
19 : : #include "ccache.h"
20 : : #include "hashutil.h"
21 : : #include "macroskip.h"
22 : : #include "murmurhashneutral2.h"
23 : :
24 : : unsigned
25 : 0 : hash_from_string(void *str)
26 : : {
27 : 0 : return murmurhashneutral2(str, strlen((const char *)str), 0);
28 : : }
29 : :
30 : : unsigned
31 : 0 : hash_from_int(int i)
32 : : {
33 : 0 : return murmurhashneutral2(&i, sizeof(int), 0);
34 : : }
35 : :
36 : : int
37 : 0 : strings_equal(void *str1, void *str2)
38 : : {
39 : 0 : return str_eq((const char *)str1, (const char *)str2);
40 : : }
41 : :
42 : : int
43 : 0 : file_hashes_equal(struct file_hash *fh1, struct file_hash *fh2)
44 : : {
45 [ # # ][ # # ]: 0 : return memcmp(fh1->hash, fh2->hash, 16) == 0
46 : 0 : && fh1->size == fh2->size;
47 : : }
48 : :
49 : : /*
50 : : * Search for the strings "__DATE__" and "__TIME__" in str.
51 : : *
52 : : * Returns a bitmask with HASH_SOURCE_CODE_FOUND_DATE and
53 : : * HASH_SOURCE_CODE_FOUND_TIME set appropriately.
54 : : */
55 : : int
56 : 34 : check_for_temporal_macros(const char *str, size_t len)
57 : : {
58 : 34 : int result = 0;
59 : :
60 : : /*
61 : : * We're using the Boyer-Moore-Horspool algorithm, which searches starting
62 : : * from the *end* of the needle. Our needles are 8 characters long, so i
63 : : * starts at 7.
64 : : */
65 : 34 : size_t i = 7;
66 : :
67 [ + + ]: 580 : while (i < len) {
68 : : /*
69 : : * Check whether the substring ending at str[i] has the form "__...E__". On
70 : : * the assumption that 'E' is less common in source than '_', we check
71 : : * str[i-2] first.
72 : : */
73 [ + + ][ + + ]: 546 : if (str[i - 2] == 'E' &&
[ + + ][ + + ]
[ + + ]
74 : 116 : str[i - 0] == '_' &&
75 : 100 : str[i - 7] == '_' &&
76 : 86 : str[i - 1] == '_' &&
77 : 70 : str[i - 6] == '_') {
78 : : /*
79 : : * Check the remaining characters to see if the substring is "__DATE__"
80 : : * or "__TIME__".
81 : : */
82 [ + + ][ + - ]: 22 : if (str[i - 5] == 'D' && str[i - 4] == 'A' &&
[ + - ]
83 : 11 : str[i - 3] == 'T') {
84 : 11 : result |= HASH_SOURCE_CODE_FOUND_DATE;
85 : : }
86 [ + - ][ + - ]: 11 : else if (str[i - 5] == 'T' && str[i - 4] == 'I' &&
[ + - ]
87 : 11 : str[i - 3] == 'M') {
88 : 11 : result |= HASH_SOURCE_CODE_FOUND_TIME;
89 : : }
90 : : }
91 : :
92 : : /*
93 : : * macro_skip tells us how far we can skip forward upon seeing str[i] at
94 : : * the end of a substring.
95 : : */
96 : 546 : i += macro_skip[(uint8_t)str[i]];
97 : : }
98 : :
99 : 34 : return result;
100 : : }
101 : :
102 : : /*
103 : : * Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results.
104 : : */
105 : : int
106 : 0 : hash_source_code_string(
107 : : struct conf *conf, struct mdfour *hash, const char *str, size_t len,
108 : : const char *path)
109 : : {
110 : 0 : int result = HASH_SOURCE_CODE_OK;
111 : :
112 : : /*
113 : : * Check for __DATE__ and __TIME__ if the sloppiness configuration tells us
114 : : * we should.
115 : : */
116 [ # # ]: 0 : if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) {
117 : 0 : result |= check_for_temporal_macros(str, len);
118 : : }
119 : :
120 : : /*
121 : : * Hash the source string.
122 : : */
123 : 0 : hash_buffer(hash, str, len);
124 : :
125 [ # # ]: 0 : if (result & HASH_SOURCE_CODE_FOUND_DATE) {
126 : : /*
127 : : * Make sure that the hash sum changes if the (potential) expansion of
128 : : * __DATE__ changes.
129 : : */
130 : 0 : time_t t = time(NULL);
131 : 0 : struct tm *now = localtime(&t);
132 : 0 : cc_log("Found __DATE__ in %s", path);
133 : 0 : hash_delimiter(hash, "date");
134 : 0 : hash_buffer(hash, &now->tm_year, sizeof(now->tm_year));
135 : 0 : hash_buffer(hash, &now->tm_mon, sizeof(now->tm_mon));
136 : 0 : hash_buffer(hash, &now->tm_mday, sizeof(now->tm_mday));
137 : : }
138 [ # # ]: 0 : if (result & HASH_SOURCE_CODE_FOUND_TIME) {
139 : : /*
140 : : * We don't know for sure that the program actually uses the __TIME__
141 : : * macro, but we have to assume it anyway and hash the time stamp. However,
142 : : * that's not very useful since the chance that we get a cache hit later
143 : : * the same second should be quite slim... So, just signal back to the
144 : : * caller that __TIME__ has been found so that the direct mode can be
145 : : * disabled.
146 : : */
147 : 0 : cc_log("Found __TIME__ in %s", path);
148 : : }
149 : :
150 : 0 : return result;
151 : : }
152 : :
153 : : /*
154 : : * Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
155 : : * results.
156 : : */
157 : : int
158 : 0 : hash_source_code_file(struct conf *conf, struct mdfour *hash, const char *path)
159 : : {
160 : : char *data;
161 : : size_t size;
162 : :
163 [ # # ]: 0 : if (is_precompiled_header(path)) {
164 [ # # ]: 0 : if (hash_file(hash, path)) {
165 : 0 : return HASH_SOURCE_CODE_OK;
166 : : } else {
167 : 0 : return HASH_SOURCE_CODE_ERROR;
168 : : }
169 : : } else {
170 : : int result;
171 : :
172 [ # # ]: 0 : if (!read_file(path, 0, &data, &size)) {
173 : 0 : return HASH_SOURCE_CODE_ERROR;
174 : : }
175 : 0 : result = hash_source_code_string(conf, hash, data, size, path);
176 : 0 : free(data);
177 : 0 : return result;
178 : : }
179 : : }
180 : :
181 : : bool
182 : 15 : hash_command_output(struct mdfour *hash, const char *command,
183 : : const char *compiler)
184 : : {
185 : : #ifdef _WIN32
186 : : SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
187 : : HANDLE pipe_out[2];
188 : : PROCESS_INFORMATION pi;
189 : : STARTUPINFO si;
190 : : DWORD exitcode;
191 : : char *sh = NULL;
192 : : char *win32args;
193 : : char *path;
194 : : BOOL ret;
195 : : bool ok;
196 : : int fd;
197 : : #else
198 : : pid_t pid;
199 : : int pipefd[2];
200 : : #endif
201 : :
202 : 15 : struct args *args = args_init_from_string(command);
203 : : int i;
204 [ + + ]: 37 : for (i = 0; i < args->argc; i++) {
205 [ + + ]: 22 : if (str_eq(args->argv[i], "%compiler%")) {
206 : 1 : args_set(args, i, compiler);
207 : : }
208 : : }
209 : 15 : cc_log_argv("Executing compiler check command ", args->argv);
210 : :
211 : : #ifdef _WIN32
212 : : memset(&pi, 0x00, sizeof(pi));
213 : : memset(&si, 0x00, sizeof(si));
214 : :
215 : : path = find_executable(args->argv[0], NULL);
216 : : if (!path)
217 : : path = args->argv[0];
218 : : sh = win32getshell(path);
219 : : if (sh)
220 : : path = sh;
221 : :
222 : : si.cb = sizeof(STARTUPINFO);
223 : : CreatePipe(&pipe_out[0], &pipe_out[1], &sa, 0);
224 : : SetHandleInformation(pipe_out[0], HANDLE_FLAG_INHERIT, 0);
225 : : si.hStdOutput = pipe_out[1];
226 : : si.hStdError = pipe_out[1];
227 : : si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
228 : : si.dwFlags = STARTF_USESTDHANDLES;
229 : : win32args = win32argvtos(sh, args->argv);
230 : : ret = CreateProcess(path, win32args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi);
231 : : CloseHandle(pipe_out[1]);
232 : : args_free(args);
233 : : free(win32args);
234 : : if (ret == 0) {
235 : : stats_update(STATS_COMPCHECK);
236 : : return false;
237 : : }
238 : : fd = _open_osfhandle((intptr_t) pipe_out[0], O_BINARY);
239 : : ok = hash_fd(hash, fd);
240 : : if (!ok) {
241 : : cc_log("Error hashing compiler check command output: %s", strerror(errno));
242 : : stats_update(STATS_COMPCHECK);
243 : : }
244 : : WaitForSingleObject(pi.hProcess, INFINITE);
245 : : GetExitCodeProcess(pi.hProcess, &exitcode);
246 : : CloseHandle(pipe_out[0]);
247 : : CloseHandle(pi.hProcess);
248 : : CloseHandle(pi.hThread);
249 : : if (exitcode != 0) {
250 : : cc_log("Compiler check command returned %d", (int) exitcode);
251 : : stats_update(STATS_COMPCHECK);
252 : : return false;
253 : : }
254 : : return ok;
255 : : #else
256 [ - + ]: 15 : if (pipe(pipefd) == -1) {
257 : 0 : fatal("pipe failed");
258 : : }
259 : 15 : pid = fork();
260 [ - + ]: 30 : if (pid == -1) {
261 : 0 : fatal("fork failed");
262 : : }
263 : :
264 [ + + ]: 30 : if (pid == 0) {
265 : : /* Child. */
266 : 15 : close(pipefd[0]);
267 : 15 : close(0);
268 : 15 : dup2(pipefd[1], 1);
269 : 15 : dup2(pipefd[1], 2);
270 : 15 : _exit(execvp(args->argv[0], args->argv));
271 : : return false; /* Never reached. */
272 : : } else {
273 : : /* Parent. */
274 : : int status;
275 : : bool ok;
276 : 15 : args_free(args);
277 : 15 : close(pipefd[1]);
278 : 15 : ok = hash_fd(hash, pipefd[0]);
279 [ - + ]: 15 : if (!ok) {
280 : 0 : cc_log("Error hashing compiler check command output: %s", strerror(errno));
281 : 0 : stats_update(STATS_COMPCHECK);
282 : : }
283 : 15 : close(pipefd[0]);
284 [ - + ]: 15 : if (waitpid(pid, &status, 0) != pid) {
285 : 0 : cc_log("waitpid failed");
286 : 0 : return false;
287 : : }
288 [ + - ][ + + ]: 15 : if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
289 : 1 : cc_log("Compiler check command returned %d", WEXITSTATUS(status));
290 : 1 : stats_update(STATS_COMPCHECK);
291 : 1 : return false;
292 : : }
293 : 15 : return ok;
294 : : }
295 : : #endif
296 : : }
297 : :
298 : : bool
299 : 3 : hash_multicommand_output(struct mdfour *hash, const char *commands,
300 : : const char *compiler)
301 : : {
302 : 3 : char *command_string, *command, *p, *saveptr = NULL;
303 : 3 : bool ok = true;
304 : :
305 : 3 : command_string = x_strdup(commands);
306 : 3 : p = command_string;
307 [ + + ]: 8 : while ((command = strtok_r(p, ";", &saveptr))) {
308 [ + + ]: 5 : if (!hash_command_output(hash, command, compiler)) {
309 : 1 : ok = false;
310 : : }
311 : 5 : p = NULL;
312 : : }
313 : 3 : free(command_string);
314 : 3 : return ok;
315 : : }
|