@@ -120,43 +120,27 @@ class DumpHangedTestPlugin : Plugin<Project> {
120120
121121 dumpsDir.mkdirs()
122122
123- fun file (name : String , ext : String = "log") =
124- File (dumpsDir , " $name - ${ System .currentTimeMillis()} . $ext " )
123+ val allProcessesFile = file(dumpsDir, " all-processes " )
124+ runCmd( Redirect .to(allProcessesFile) , " ps " , " -ef " )
125125
126- // Collect all JVMs pids.
127- val allJavaProcessesFile = file(" all-java-processes" )
128- runCmd(Redirect .to(allJavaProcessesFile), " jcmd" , " -l" )
126+ val allProcesses = extractProcesses(allProcessesFile)
129127
130- // On IBM JDK thread dump can be collected by signaling the matching `Gradle Test Executor` process with `kill -3`.
131- // It will be writen into `/tmp/javacore.YYYYMMDD.HHMMSS.PID.SEQ.txt
132- if (isIbm8(allJavaProcessesFile)) {
133- val allProcessesFile = file(" all-processes" )
134- runCmd(Redirect .to(allProcessesFile), " ps" , " -ef" )
135- extractPidsIbm8(allProcessesFile).forEach { ibm8Pid ->
136- runCmd(Redirect .INHERIT , " kill" , " -3" , ibm8Pid)
137- }
138- } else {
139- val pids = extractPids(allJavaProcessesFile)
128+ val gradleTestExecutors = allProcesses.filter { it.command.contains(" Gradle Test Executor" ) }
129+ val childProcesses = collectChildProcesses(allProcesses, gradleTestExecutors)
140130
141- pids.forEach { pid ->
142- // Collect heap dump by pid.
143- val heapDumpPath = file(" ${pid} -heap-dump" , " hprof" ).absolutePath
144- runCmd(Redirect .INHERIT , " jcmd" , pid, " GC.heap_dump" , heapDumpPath)
145-
146- // Collect thread dump by pid.
147- val threadDumpFile = file(" ${pid} -thread-dump" )
148- runCmd(Redirect .to(threadDumpFile), " jcmd" , pid, " Thread.print" , " -l" )
149- }
131+ (gradleTestExecutors + childProcesses).forEach { process -> collectDump(dumpsDir, process) }
150132
151- // Just in case collect all thread dumps by using special PID `0`.
152- val allThreadsFile = file(" all-thread-dumps" )
153- runCmd(Redirect .to(allThreadsFile), " jcmd" , " 0" , " Thread.print" , " -l" )
154- }
133+ // Just in case collect all thread dumps by using special PID `0`.
134+ val allThreadsFile = file(dumpsDir, " all-thread-dumps" )
135+ runCmd(Redirect .to(allThreadsFile), " jcmd" , " 0" , " Thread.print" , " -l" )
155136 } catch (e: Throwable ) {
156137 t.logger.warn(" Taking dumps failed with error: ${e.message ? : e.javaClass.name} , for ${t.path} " )
157138 }
158139 }
159140
141+ private fun file (baseDir : File , name : String , ext : String = "log") =
142+ File (baseDir, " $name -${System .currentTimeMillis()} .$ext " )
143+
160144 private fun cleanup (t : Task ) {
161145 val future = t.extra
162146 .takeIf { it.has(DUMP_FUTURE_KEY ) }
@@ -183,23 +167,55 @@ class DumpHangedTestPlugin : Plugin<Project> {
183167 }
184168 }
185169
186- private fun isIbm8 (file : File ): Boolean =
187- file.readLines().any { it.contains(" -PtestJvm=ibm8" ) }
170+ private data class ProcessInfo (
171+ val pid : String ,
172+ val ppid : String ,
173+ val command : String ,
174+ val isIbm : Boolean
175+ )
188176
189- private fun extractPids (file : File ): List <String > =
190- file.readLines()
191- .filter { it.contains(" Gradle Test Executor" ) }
192- .map { it.substringBefore(' ' ) }
177+ private val whitespaceRegex = Regex (" \\ s+" )
193178
194- private fun extractPidsIbm8 (file : File ): List <String > =
179+ // ps -ef format produce output like: `UID PID PPID C STIME TTY TIME CMD`
180+ private fun extractProcesses (file : File ): List <ProcessInfo > =
195181 file.readLines()
196- .filter { it.contains(" Gradle Test Executor" ) }
197- .filter { it.contains(" ibm" , ignoreCase = true ) }
198- .mapNotNull(::extractPid)
182+ .filter { it.contains(" /bin/java" ) }
183+ .map {
184+ val parts = it.trimStart().split(whitespaceRegex, limit = 8 )
185+ val command = parts.getOrNull(7 ) ? : " "
186+
187+ ProcessInfo (
188+ pid = parts[1 ],
189+ ppid = parts[2 ],
190+ command = command,
191+ isIbm = command.contains(" /ibm8" )
192+ )
193+ }
199194
200- private val whitespaceRegex = Regex (" \\ s+" )
195+ private fun collectChildProcesses (
196+ allProcesses : List <ProcessInfo >,
197+ gradleTestExecutors : List <ProcessInfo >
198+ ): List <ProcessInfo > {
199+ val parentPids = gradleTestExecutors.map { it.pid }.toSet()
200+ return allProcesses.filter { parentPids.contains(it.ppid) }
201+ }
201202
202- // ps -ef format produce output like: UID PID PPID ...
203- private fun extractPid (line : String ): String? =
204- line.trimStart().split(whitespaceRegex, limit = 3 ).getOrNull(1 )
203+ private fun collectDump (
204+ baseDir : File ,
205+ process : ProcessInfo
206+ ) {
207+ if (process.isIbm) {
208+ // On IBM JDK thread dump can be collected by signaling process with `kill -3`.
209+ // It will be writen into `/tmp/javacore.YYYYMMDD.HHMMSS.PID.SEQ.txt
210+ runCmd(Redirect .INHERIT , " kill" , " -3" , process.pid)
211+ } else {
212+ // Collect heap dump by pid.
213+ val heapDumpPath = file(baseDir, " ${process.pid} -heap-dump" , " hprof" ).absolutePath
214+ runCmd(Redirect .INHERIT , " jcmd" , process.pid, " GC.heap_dump" , heapDumpPath)
215+
216+ // Collect thread dump by pid.
217+ val threadDumpFile = file(baseDir, " ${process.pid} -thread-dump" , " log" )
218+ runCmd(Redirect .to(threadDumpFile), " jcmd" , process.pid, " Thread.print" , " -l" )
219+ }
220+ }
205221}
0 commit comments