OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/test/launcher/parallel_test_launcher.h" | |
6 | |
7 #if defined(OS_POSIX) | |
8 #include <fcntl.h> | |
9 #include <sys/stat.h> | |
10 #include <sys/types.h> | |
11 #endif | |
12 | |
13 #if defined(OS_WIN) | |
14 #include <windows.h> | |
15 #endif | |
16 | |
17 #include "base/bind.h" | |
18 #include "base/command_line.h" | |
19 #include "base/file_util.h" | |
20 #include "base/location.h" | |
21 #include "base/message_loop/message_loop_proxy.h" | |
22 #include "base/process/launch.h" | |
23 #include "base/run_loop.h" | |
24 #include "base/test/launcher/test_launcher.h" | |
25 #include "base/test/sequenced_worker_pool_owner.h" | |
26 | |
27 #if defined(OS_WIN) | |
28 #include "base/win/scoped_handle.h" | |
29 #endif | |
30 | |
31 namespace base { | |
32 | |
33 namespace { | |
34 | |
35 // Maximum time of no output after which we print list of processes still | |
36 // running. This deliberately doesn't use TestTimeouts (which is otherwise | |
37 // a recommended solution), because they can be increased. This would defeat | |
38 // the purpose of this timeout, which is 1) to avoid buildbot "no output for | |
39 // X seconds" timeout killing the process 2) help communicate status of | |
40 // the test launcher to people looking at the output (no output for a long | |
41 // time is mysterious and gives no info about what is happening) 3) help | |
42 // debugging in case the process hangs anyway. | |
43 const int kOutputTimeoutSeconds = 15; | |
44 | |
45 void RunCallback( | |
46 const ParallelTestLauncher::LaunchChildGTestProcessCallback& callback, | |
47 int exit_code, | |
48 const TimeDelta& elapsed_time, | |
49 bool was_timeout, | |
50 const std::string& output) { | |
51 callback.Run(exit_code, elapsed_time, was_timeout, output); | |
52 } | |
53 | |
54 void DoLaunchChildTestProcess( | |
55 const CommandLine& command_line, | |
56 base::TimeDelta timeout, | |
57 scoped_refptr<MessageLoopProxy> message_loop_proxy, | |
58 const ParallelTestLauncher::LaunchChildGTestProcessCallback& callback) { | |
59 TimeTicks start_time = TimeTicks::Now(); | |
60 | |
61 // Redirect child process output to a file. | |
62 base::FilePath output_file; | |
63 CHECK(file_util::CreateTemporaryFile(&output_file)); | |
64 | |
65 LaunchOptions options; | |
66 #if defined(OS_WIN) | |
67 // Make the file handle inheritable by the child. | |
68 SECURITY_ATTRIBUTES sa_attr; | |
69 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); | |
70 sa_attr.lpSecurityDescriptor = NULL; | |
71 sa_attr.bInheritHandle = TRUE; | |
72 | |
73 win::ScopedHandle handle(CreateFile(output_file.value().c_str(), | |
74 GENERIC_WRITE, | |
75 FILE_SHARE_READ | FILE_SHARE_DELETE, | |
76 &sa_attr, | |
77 OPEN_EXISTING, | |
78 FILE_ATTRIBUTE_TEMPORARY, | |
79 NULL)); | |
80 CHECK(handle.IsValid()); | |
81 options.inherit_handles = true; | |
82 options.stdin_handle = INVALID_HANDLE_VALUE; | |
83 options.stdout_handle = handle.Get(); | |
84 options.stderr_handle = handle.Get(); | |
85 #elif defined(OS_POSIX) | |
86 options.new_process_group = true; | |
87 | |
88 int output_file_fd = open(output_file.value().c_str(), O_RDWR); | |
89 CHECK_GE(output_file_fd, 0); | |
90 | |
91 file_util::ScopedFD output_file_fd_closer(&output_file_fd); | |
92 | |
93 base::FileHandleMappingVector fds_mapping; | |
94 fds_mapping.push_back(std::make_pair(output_file_fd, STDOUT_FILENO)); | |
95 fds_mapping.push_back(std::make_pair(output_file_fd, STDERR_FILENO)); | |
96 options.fds_to_remap = &fds_mapping; | |
97 #endif | |
98 | |
99 bool was_timeout = false; | |
100 int exit_code = LaunchChildTestProcessWithOptions( | |
101 command_line, options, timeout, &was_timeout); | |
102 | |
103 #if defined(OS_WIN) | |
104 FlushFileBuffers(handle.Get()); | |
105 handle.Close(); | |
106 #elif defined(OS_POSIX) | |
107 output_file_fd_closer.reset(); | |
108 #endif | |
109 | |
110 std::string output_file_contents; | |
111 CHECK(base::ReadFileToString(output_file, &output_file_contents)); | |
112 | |
113 if (!base::DeleteFile(output_file, false)) { | |
114 // This needs to be non-fatal at least for Windows. | |
115 LOG(WARNING) << "Failed to delete " << output_file.AsUTF8Unsafe(); | |
116 } | |
117 | |
118 // Run target callback on the thread it was originating from, not on | |
119 // a worker pool thread. | |
120 message_loop_proxy->PostTask( | |
121 FROM_HERE, | |
122 Bind(&RunCallback, | |
123 callback, | |
124 exit_code, | |
125 TimeTicks::Now() - start_time, | |
126 was_timeout, | |
127 output_file_contents)); | |
128 } | |
129 | |
130 } // namespace | |
131 | |
132 ParallelTestLauncher::ParallelTestLauncher(size_t jobs) | |
133 : timer_(FROM_HERE, | |
134 TimeDelta::FromSeconds(kOutputTimeoutSeconds), | |
135 this, | |
136 &ParallelTestLauncher::OnOutputTimeout), | |
137 launch_sequence_number_(0), | |
138 worker_pool_owner_( | |
139 new SequencedWorkerPoolOwner(jobs, "parallel_test_launcher")) { | |
140 // Start the watchdog timer. | |
141 timer_.Reset(); | |
142 } | |
143 | |
144 ParallelTestLauncher::~ParallelTestLauncher() { | |
145 DCHECK(thread_checker_.CalledOnValidThread()); | |
146 | |
147 worker_pool_owner_->pool()->Shutdown(); | |
148 } | |
149 | |
150 void ParallelTestLauncher::LaunchChildGTestProcess( | |
151 const CommandLine& command_line, | |
152 const std::string& wrapper, | |
153 base::TimeDelta timeout, | |
154 const LaunchChildGTestProcessCallback& callback) { | |
155 DCHECK(thread_checker_.CalledOnValidThread()); | |
156 | |
157 // Record the exact command line used to launch the child. | |
158 CommandLine new_command_line( | |
159 PrepareCommandLineForGTest(command_line, wrapper)); | |
160 launch_sequence_number_++; | |
161 running_processes_map_.insert( | |
162 std::make_pair(launch_sequence_number_, new_command_line)); | |
163 | |
164 worker_pool_owner_->pool()->PostWorkerTask( | |
165 FROM_HERE, | |
166 Bind(&DoLaunchChildTestProcess, | |
167 new_command_line, | |
168 timeout, | |
169 MessageLoopProxy::current(), | |
170 Bind(&ParallelTestLauncher::OnLaunchTestProcessFinished, | |
171 Unretained(this), | |
172 launch_sequence_number_, | |
173 callback))); | |
174 } | |
175 | |
176 void ParallelTestLauncher::ResetOutputWatchdog() { | |
177 DCHECK(thread_checker_.CalledOnValidThread()); | |
178 timer_.Reset(); | |
179 } | |
180 | |
181 void ParallelTestLauncher::OnLaunchTestProcessFinished( | |
182 size_t sequence_number, | |
183 const LaunchChildGTestProcessCallback& callback, | |
184 int exit_code, | |
185 const TimeDelta& elapsed_time, | |
186 bool was_timeout, | |
187 const std::string& output) { | |
188 DCHECK(thread_checker_.CalledOnValidThread()); | |
189 running_processes_map_.erase(sequence_number); | |
190 callback.Run(exit_code, elapsed_time, was_timeout, output); | |
191 } | |
192 | |
193 void ParallelTestLauncher::OnOutputTimeout() { | |
194 DCHECK(thread_checker_.CalledOnValidThread()); | |
195 | |
196 fprintf(stdout, "Still waiting for the following processes to finish:\n"); | |
197 | |
198 for (RunningProcessesMap::const_iterator i = running_processes_map_.begin(); | |
199 i != running_processes_map_.end(); | |
200 ++i) { | |
201 #if defined(OS_WIN) | |
202 fwprintf(stdout, L"\t%s\n", i->second.GetCommandLineString().c_str()); | |
203 #else | |
204 fprintf(stdout, "\t%s\n", i->second.GetCommandLineString().c_str()); | |
205 #endif | |
206 } | |
207 | |
208 fflush(stdout); | |
209 | |
210 // Arm the timer again - otherwise it would fire only once. | |
211 timer_.Reset(); | |
212 } | |
213 | |
214 } // namespace base | |
OLD | NEW |