-
Notifications
You must be signed in to change notification settings - Fork 28.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPARK-9516][UI] Improvement of Thread Dump Page #7910
Changes from 12 commits
4843c40
4ec95fa
64c470d
26001ab
1b35ee9
f2ff69d
8b8089e
33bb3e3
1386791
db7b54b
c3dc9b7
e88f70d
8bfb4bc
6106c7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,3 +30,75 @@ function stripeSummaryTable() { | |
} | ||
}); | ||
} | ||
|
||
function toggleThreadStackTrace(threadId, forceAdd) { | ||
var stackTrace = $("#" + threadId + "_stacktrace") | ||
if (stackTrace.length == 0) { | ||
var stackTraceText = $('#' + threadId + "_td_stacktrace").html() | ||
var threadCell = $("#thread_" + threadId + "_tr") | ||
threadCell.after("<tr id=\"" + threadId +"_stacktrace\" class=\"accordion-body\"><td colspan=\"3\"><pre>" + | ||
stackTraceText + "</pre></td></tr>") | ||
} else { | ||
if (!forceAdd) { | ||
stackTrace.remove() | ||
} | ||
} | ||
} | ||
|
||
// expandOrCollapse - true: expand, false: collapse | ||
function expandOrCollapseAllThreadStackTrace(expandOrCollapse, toggleButton) { | ||
if (expandOrCollapse) { | ||
$('.accordion-heading').each(function() { | ||
//get thread ID | ||
if (!$(this).hasClass("hidden")) { | ||
var trId = $(this).attr('id').match(/thread_([0-9]+)_tr/m)[1] | ||
toggleThreadStackTrace(trId, true) | ||
} | ||
}) | ||
if (toggleButton) { | ||
$('.expandbutton').toggleClass('hidden') | ||
} | ||
} else { | ||
$('.accordion-body').each(function() { | ||
$(this).remove() | ||
}) | ||
if (toggleButton) { | ||
$('.expandbutton').toggleClass('hidden'); | ||
} | ||
} | ||
} | ||
|
||
// inOrOut - true: over, false: out | ||
function onMouseOverAndOut(threadId) { | ||
$("#" + threadId + "_td_id").toggleClass("threaddump-td-mouseover"); | ||
$("#" + threadId + "_td_name").toggleClass("threaddump-td-mouseover"); | ||
$("#" + threadId + "_td_state").toggleClass("threaddump-td-mouseover"); | ||
} | ||
|
||
function onSearchStringChange() { | ||
var searchString = $('#search').val() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a |
||
//remove the stacktrace | ||
expandOrCollapseAllThreadStackTrace(false, false) | ||
if (searchString.length == 0) { | ||
$('tr').each(function() { | ||
$(this).removeClass('hidden') | ||
}) | ||
} else { | ||
$('tr').each(function(){ | ||
if($(this).attr('id') && $(this).attr('id').match(/thread_[0-9]+_tr/) ) { | ||
var children = $(this).children() | ||
var found = false | ||
for (i = 0; i < children.length; i++) { | ||
if (children.eq(i).text().toLowerCase().indexOf(searchString) >= 0) { | ||
found = true | ||
} | ||
} | ||
if (found) { | ||
$(this).removeClass('hidden') | ||
} else { | ||
$(this).addClass('hidden') | ||
} | ||
} | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ | |
package org.apache.spark.ui.exec | ||
|
||
import java.net.URLDecoder | ||
import java.util.regex.Pattern | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I think this is unused now |
||
import javax.servlet.http.HttpServletRequest | ||
|
||
import scala.util.Try | ||
|
@@ -49,7 +50,7 @@ private[ui] class ExecutorThreadDumpPage(parent: ExecutorsTab) extends WebUIPage | |
val maybeThreadDump = sc.get.getExecutorThreadDump(executorId) | ||
|
||
val content = maybeThreadDump.map { threadDump => | ||
val dumpRows = threadDump.sortWith { | ||
val dumpRows = threadDump.sortWith { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: old indentation was correct |
||
case (threadTrace1, threadTrace2) => { | ||
val v1 = if (threadTrace1.threadName.contains("Executor task launch")) 1 else 0 | ||
val v2 = if (threadTrace2.threadName.contains("Executor task launch")) 1 else 0 | ||
|
@@ -60,44 +61,49 @@ private[ui] class ExecutorThreadDumpPage(parent: ExecutorsTab) extends WebUIPage | |
} | ||
} | ||
}.map { thread => | ||
val threadName = thread.threadName | ||
val className = "accordion-heading " + { | ||
if (threadName.contains("Executor task launch")) { | ||
"executor-thread" | ||
} else { | ||
"non-executor-thread" | ||
} | ||
} | ||
<div class="accordion-group"> | ||
<div class={className} onclick="$(this).next().toggleClass('hidden')"> | ||
<a class="accordion-toggle"> | ||
Thread {thread.threadId}: {threadName} ({thread.threadState}) | ||
</a> | ||
</div> | ||
<div class="accordion-body hidden"> | ||
<div class="accordion-inner"> | ||
<pre>{thread.stackTrace}</pre> | ||
val threadId = thread.threadId | ||
<tr id={s"thread_${threadId}_tr"} class="accordion-heading" | ||
onclick={s"toggleThreadStackTrace($threadId, false)"} | ||
onmouseover={s"onMouseOverAndOut($threadId)"} | ||
onmouseout={s"onMouseOverAndOut($threadId)"}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're losing the highlighting of the task threads here. A user could search for the thread name, but I don't think users will know that is what they should search for. I can't think of a clean UI treatment to keep this, so I'm OK removing it, but I just wanted to call it out in case others have ideas / opinions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is just OK, since when the user opens the thread dump page, the task threads are always listed as the top rows of the table, |
||
<td id={s"${threadId}_td_id"}>{threadId}</td> | ||
<td id={s"${threadId}_td_name"}>{thread.threadName}</td> | ||
<td id={s"${threadId}_td_state"}>{thread.threadState}</td> | ||
<td id={s"${threadId}_td_stacktrace"} class="hidden">{thread.stackTrace}</td> | ||
</tr> | ||
} | ||
|
||
<div class="row-fluid"> | ||
<p>Updated at {UIUtils.formatDate(time)}</p> | ||
{ | ||
// scalastyle:off | ||
<p><a class="expandbutton" onClick="expandOrCollapseAllThreadStackTrace(true, true)"> | ||
Expand All | ||
</a></p> | ||
<p><a class="expandbutton hidden" onClick="expandOrCollapseAllThreadStackTrace(false, true)"> | ||
Collapse All | ||
</a></p> | ||
<div class="form-inline"> | ||
<div class="bs-example" data-example-id="simple-form-inline"> | ||
<div class="form-group"> | ||
<div class="input-group"> | ||
Search: <input type="text" class="form-control" id="search" oninput="onSearchStringChange()"></input> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<p></p> | ||
// scalastyle:on | ||
} | ||
|
||
<div class="row-fluid"> | ||
<p>Updated at {UIUtils.formatDate(time)}</p> | ||
{ | ||
// scalastyle:off | ||
<p><a class="expandbutton" | ||
onClick="$('.accordion-body').removeClass('hidden'); $('.expandbutton').toggleClass('hidden')"> | ||
Expand All | ||
</a></p> | ||
<p><a class="expandbutton hidden" | ||
onClick="$('.accordion-body').addClass('hidden'); $('.expandbutton').toggleClass('hidden')"> | ||
Collapse All | ||
</a></p> | ||
// scalastyle:on | ||
} | ||
<div class="accordion">{dumpRows}</div> | ||
</div> | ||
<table class={UIUtils.TABLE_CLASS_STRIPED + " accordion-group" + " sortable"}> | ||
<thead> | ||
<th onClick="expandOrCollapseAllThreadStackTrace(false, false)">Thread ID</th> | ||
<th onClick="expandOrCollapseAllThreadStackTrace(false, false)">Thread Name</th> | ||
<th onClick="expandOrCollapseAllThreadStackTrace(false, false)">Thread State</th> | ||
</thead> | ||
<tbody>{dumpRows}</tbody> | ||
</table> | ||
</div> | ||
}.getOrElse(Text("Error fetching thread dump")) | ||
UIUtils.headerSparkPage(s"Thread dump for executor $executorId", content, parent) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so I am totally unqualified to be judging javascript code, so feel free to reject this suggestion -- but to me it kinda seems like this really two different functions based on
expandOrCollapse
. The callsite always statically knows the value of that param, so it doesn't help to put them together. Might be cleaner to separate them out? Also it seems like its never called withtrue, false
, so then you could eliminate that option.(But again, totally up to your judgement, I have an incomplete understanding of what is going on here ...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm...good suggestion, it looks fishy to me too....