/*
* Copyright 2015 the original author or authors.
* @https://github.com/scouter-project/scouter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package scouter.client.stack.base;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import scouter.client.Images;
import scouter.client.PerspectiveStackAnalyzer;
import scouter.client.stack.config.ParserConfig;
import scouter.client.stack.config.ParserConfigReader;
import scouter.client.stack.config.XMLReader;
import scouter.client.stack.data.StackAnalyzedInfo;
import scouter.client.stack.data.StackAnalyzedValue;
import scouter.client.stack.data.StackAnalyzedValueComp;
import scouter.client.stack.data.StackFileInfo;
import scouter.client.stack.data.StackParser;
import scouter.client.stack.data.UniqueStackValue;
import scouter.client.stack.utils.HtmlUtils;
import scouter.client.stack.utils.ResourceUtils;
import scouter.client.stack.utils.StringUtils;
import scouter.client.stack.utils.ValueObject;
import scouter.client.stack.views.StackAnalyzerView;
public class MainProcessor{
static private MainProcessor m_mainProcessor = null;
private StackAnalyzerView m_stackAnalyzerView = null;
private Composite m_parentComposite = null;
private boolean m_isExcludeStack = true;
private boolean m_isRemoveLine = false;
private boolean m_isInerPercent = false;
private boolean m_isSortByFunction = false;
private boolean m_isSimpleDumpTimeList = true;
private boolean m_isDefaultConfiguration = false;
private boolean m_isAnalyzeAllThreads = false;
static {
m_mainProcessor = new MainProcessor();
}
static public MainProcessor instance(){
return m_mainProcessor;
}
public StackAnalyzerView getStackAnalyzerView() {
return m_stackAnalyzerView;
}
public void setStackAnalyzerView(StackAnalyzerView stackAnalyzerView) {
m_stackAnalyzerView = stackAnalyzerView;
}
public void setParentComposite(Composite parent){
m_parentComposite = parent;
}
public Composite getParentComposite(){
return m_parentComposite;
}
public boolean isExcludeStack(){
return m_isExcludeStack;
}
public void setExcludeStack(boolean value) {
m_isExcludeStack = value;
}
public boolean isRemoveLine(){
return m_isRemoveLine;
}
public void setRemoveLine(boolean value) {
m_isRemoveLine = value;
}
public boolean isInerPercent(){
return m_isInerPercent;
}
public void setInerPercent(boolean value) {
m_isInerPercent = value;
}
public boolean isSortByFunction(){
return m_isSortByFunction;
}
public void setSortByFunction(boolean value) {
m_isSortByFunction = value;
}
public boolean isSimpleDumpTimeList(){
return m_isSimpleDumpTimeList ;
}
public void setSimpleDumpTimeList(boolean value) {
m_isSimpleDumpTimeList = value;
}
public boolean isDefaultConfiguration(){
return m_isDefaultConfiguration;
}
public void setDefaultConfiguration(boolean value) {
m_isDefaultConfiguration = value;
}
public boolean isAnalyzeAllThreads(){
return m_isAnalyzeAllThreads;
}
public void setAnalyzeAllThread(boolean value){
m_isAnalyzeAllThreads = value;
}
public void processMenu(String menuName){
System.out.println("Selected:" + menuName);
if ( menuName.substring(1, 3).equals(":\\") ) {
// openFiles(new File[] { new File(menuName) }, true);
} else if ( "Open Stack Log".equals(menuName) ) {
chooseStackFile();
} else if ( "Open Analyzed Stack Log".equals(menuName) ) {
openAnalyzedInfo();
} else if ( "Close All".equals(menuName) ) {
closeStackAllFileInfo();
} else if ( "Select Parser Configuration".equals(menuName) ) {
selectCurrentParserConfig();
} else if ( "Manual Performance Tree(Ascending)".equals(menuName) ) {
createManualJob(FilterInputDialog.TASK.PERFORMANCE_TREE, true);
} else if ( "Manual Performance Tree(Descending)".equals(menuName) ) {
createManualJob(FilterInputDialog.TASK.PERFORMANCE_TREE, false);
} else if ( "Manual Service Call".equals(menuName) ) {
createManualJob(FilterInputDialog.TASK.SERVICE_CALL, true);
} else if ( "Manual Thread Stack (max 20000 lines)".equals(menuName) ) {
createManualJob(FilterInputDialog.TASK.THREAD_STACK, true);
} else if ( "Manual Stack Analyze(Include)".equals(menuName) ) {
createManualJob(FilterInputDialog.TASK.FILTER_ANALYZER, true);
} else if ( "Manual Stack Analyze(Exclude)".equals(menuName) ) {
createManualJob(FilterInputDialog.TASK.FILTER_ANALYZER, false);
// Main Tree Popup menu
} else if ( "Performance Tree".equals(menuName) ) {
createMainPerformance();
} else if ( "Close".equals(menuName) ) {
closeStackFileInfo();
} else if ( "Reanalyze".equals(menuName) ) {
reanalyzeStackFileInfo();
} else if ( "View Raw Index File".equals(menuName) ) {
viewRawIndexFile();
// Main Table Popup menu
} else if ( "Performance Tree(Ascending)".equals(menuName) ) {
createAnalyzedPerformance(true);
} else if ( "Performance Tree(Descending)".equals(menuName) ) {
createAnalyzedPerformance(false);
} else if ( "View Thread Stack (max 20000 lines)".equals(menuName)) {
viewThreadStack();
} else if ( "View Service Call".equals(menuName) ) {
viewServiceCall();
} else if ( "Filter Stack Analyze".equals(menuName) ) {
analyzeFilterStack(null);
} else if ( "Copy Function".equals(menuName) ) {
copyFunctionName();
}
}
private void chooseStackFile() {
PreferenceManager prefManager = PreferenceManager.get();
String fileName = ResourceUtils.selectFileDialog(m_parentComposite, "Stack Log File", new String [] {"Stack Log Files", "All Files"}, new String [] {"*.log;*.txt;*.gz;*.stack", "*.*"});
if(fileName == null){
return;
}
File file = new File(fileName);
prefManager.setSelectedPath(file.getParentFile());
openFile(file, false);
}
private ParserConfig selectAdoptiveParserConfig(){
PreferenceManager prefManager = PreferenceManager.get();
String configFile = prefManager.getCurrentParserConfig();
if(m_isDefaultConfiguration){
configFile = XMLReader.DEFAULT_XMLCONFIG;
}
if (configFile == null ) {
MessageBox messageBox = new MessageBox(m_parentComposite.getShell(), SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.APPLICATION_MODAL);
messageBox.setText("Check Setting selection");
messageBox.setMessage("The configuration file is not selected.\r\nDo you want to use the default configuration?");
int result = messageBox.open();
if(result == SWT.YES){
configFile = XMLReader.DEFAULT_XMLCONFIG;
}else{
configFile = selectCurrentParserConfig();
if ( configFile == null ) {
throw new RuntimeException("Parser config file is not selected!");
}
}
}
ParserConfigReader reader = new ParserConfigReader(configFile);
return reader.read();
}
private void addProcessedStack(StackFileInfo stackFileInfo){
if ( stackFileInfo == null ){
return;
}
addMainTree(stackFileInfo);
displayContent(null);
}
private void openContents( String contents) {
ParserConfig config = selectAdoptiveParserConfig();
StackFileInfo stackFileInfo = processStackContents(contents, config, null, false, true);
addProcessedStack(stackFileInfo);
}
private void openFile( File file, boolean isRecent ) {
ParserConfig config = selectAdoptiveParserConfig();
StackFileInfo stackFileInfo = processStackFile(file.getAbsolutePath(), config, null, isRecent, true);
addProcessedStack(stackFileInfo);
}
private void openAnalyzedInfo() {
ArrayList<String> list = new ArrayList<String>();
list.add(StackParser.INFO_EXTENSION);
String filename = ResourceUtils.selectFileDialog(m_parentComposite, "Analyzed Info File", new String [] {"Stack Analyzed Files", "All Files"}, new String [] {"*.info", "*.*"});
if ( filename == null )
return;
openAnalyzedFile(filename);
}
private void openAnalyzedFile( String filename ) {
StackFileInfo fileInfo = StackParser.loadAnalyzedInfo(filename);
addProcessedStack(fileInfo);
}
private StackFileInfo processStackContents( String contents, ParserConfig config, String filter, boolean isRecent, boolean isInclude ) {
StackFileInfo stackFileInfo = new StackFileInfo("Stacks");
return processStackContents(contents, stackFileInfo, config, filter, isRecent, isInclude);
}
private StackFileInfo processStackFile( String stackFilename, ParserConfig config, String filter, boolean isRecent, boolean isInclude ) {
StackFileInfo stackFileInfo = new StackFileInfo(stackFilename);
return processStackFile(stackFileInfo, config, filter, isRecent, isInclude);
}
private StackFileInfo postSTackFile(StackFileInfo stackFileInfo, ParserConfig config, String filter, boolean isRecent){
if ( stackFileInfo.getTotalWorkingCount() <= 0 ) {
MessageBox messageBox = new MessageBox(m_parentComposite.getShell(), SWT.ICON_ERROR | SWT.YES | SWT.APPLICATION_MODAL);
messageBox.setText("File open error");
messageBox.setMessage(new StringBuilder(200).append("A working thread is not exists in ").append(stackFileInfo.getFilename()).append(". configure a ").append(config.getConfigFilename()).append(". ").toString());
messageBox.open();
return null;
}
if ( !isRecent){
PreferenceManager prefManager = PreferenceManager.get();
if(filter == null ) {
prefManager.addToStackFiles(stackFileInfo.getFilename());
}
prefManager.addToAnalyzedStackFiles(stackFileInfo.getFilename());
}
return stackFileInfo;
}
private StackFileInfo processStackContents(String contents, StackFileInfo stackFileInfo, ParserConfig config, String filter, boolean isRecent, boolean isInclude ) {
try {
StackParser parser = StackParser.getParser(config, filter, isInclude);
parser.setStackContents(contents);
parser.analyze(stackFileInfo);
stackFileInfo = postSTackFile(stackFileInfo,config, filter, isRecent);
} catch ( RuntimeException ex ) {
StackParser.removeAllAnalyzedFile(stackFileInfo);
throw ex;
}
return stackFileInfo;
}
private StackFileInfo processStackFile( StackFileInfo stackFileInfo, ParserConfig config, String filter, boolean isRecent, boolean isInclude ) {
try {
StackParser parser = StackParser.getParser(config, filter, isInclude);
parser.analyze(stackFileInfo);
stackFileInfo = postSTackFile(stackFileInfo,config, filter, isRecent);
} catch ( RuntimeException ex ) {
StackParser.removeAllAnalyzedFile(stackFileInfo);
throw ex;
}
return stackFileInfo;
}
private String selectCurrentParserConfig() {
String fileName = ResourceUtils.selectFileDialog(m_parentComposite, "XML Parser Configuration", new String [] {"XML config", "All Files"}, new String [] {"*.xml", "*.*"});
if(fileName != null){
PreferenceManager.get().setCurrentParserConfig(fileName);
displayContent(null);
}
return fileName;
}
public Browser getBrowser(){
if(m_stackAnalyzerView == null)
return null;
return m_stackAnalyzerView.getBrowser();
}
public Tree getMainTree(){
if(m_stackAnalyzerView == null)
return null;
return m_stackAnalyzerView.getMainTree();
}
public Table getTable(){
if(m_stackAnalyzerView == null)
return null;
return m_stackAnalyzerView.getTable();
}
public void displayContent( String textHtml ) {
Browser browser = getBrowser();
if ( textHtml != null ) {
browser.setText(textHtml);
} else {
browser.setText(HtmlUtils.getDefaultBody());
}
}
private void addMainTree( StackFileInfo stackFileInfo ) {
Tree tree = getMainTree();
TreeItem item = new TreeItem(tree, SWT.NONE);
item.setText(stackFileInfo.toTreeInfo());
item.setImage(Images.thread);
item.setData(stackFileInfo);
addMainTreeSubItem(item,stackFileInfo.getStackAnalyzedInfoList());
item.setExpanded(true);
}
private void addMainTreeSubItem(TreeItem parent, ArrayList<StackAnalyzedInfo> list){
TreeItem subItem;
for(StackAnalyzedInfo info : list){
subItem = new TreeItem(parent, SWT.NONE);
subItem.setText(info.toTreeInfo());
subItem.setImage(Images.grid);
subItem.setData(info);
}
}
public void setTable(StackAnalyzedInfo stackAnalyzedInfo){
clearTable();
Table table = getTable();
if(stackAnalyzedInfo == null){
return;
}
ArrayList<StackAnalyzedValue> list = null;
if ( m_isSortByFunction ) {
list = StackAnalyzedValueComp.sortClone(stackAnalyzedInfo.getAnalyzedList(), false);
}else{
list = stackAnalyzedInfo.getAnalyzedList();
}
TableItem item;
for(StackAnalyzedValue value : list){
item = new TableItem(table, SWT.BORDER);
item.setText(value.toTableInfo());
if(value instanceof UniqueStackValue){
item.setData(((UniqueStackValue)value).getStack());
}
}
}
private void closeStackAllFileInfo() {
Tree tree = this.getMainTree();
if ( tree != null ){
tree.clearAll(true);
tree.setItemCount(0);
}
clearTable();
this.displayContent(null);
}
public void clearTable(){
Table table = getTable();
if(table != null){
table.clearAll();
table.setItemCount(0);
}
}
public TreeItem getSelectedItemFromMainTree(){
Tree tree = this.getMainTree();
if(tree == null){
return null;
}
TreeItem [] items = tree.getSelection();
if(items == null || items.length == 0){
return null;
}
return items[0];
}
public Object getSelectedFromMainTree(){
TreeItem item = getSelectedItemFromMainTree();
if(item == null){
return null;
}
return item.getData();
}
public TreeItem getSelectedRootItemFromMainTree(){
Tree tree = this.getMainTree();
if(tree == null){
return null;
}
TreeItem [] items = tree.getSelection();
if(items == null || items.length == 0){
return null;
}
if(items[0].getData() instanceof StackAnalyzedInfo){
return items[0].getParentItem();
}
return items[0];
}
public StackFileInfo getSelectedStackFileInfo() {
Object object =getSelectedFromMainTree();
StackFileInfo stackFileInfo = null;
if ( object instanceof StackAnalyzedInfo ) {
stackFileInfo = ((StackAnalyzedInfo)object).getStackFileInfo();
} else if ( object instanceof StackFileInfo ) {
stackFileInfo = ((StackFileInfo)object);
}
return stackFileInfo;
}
private void createMainPerformance() {
StackFileInfo stackFileInfo = getSelectedStackFileInfo();
if ( stackFileInfo != null ){
new PerformanceWindow(m_parentComposite.getShell(), stackFileInfo, null, m_isExcludeStack, true, m_isRemoveLine, m_isInerPercent);
}
}
private void viewRawIndexFile(){
Object object = getSelectedFromMainTree();
if(object == null){
return;
}
if (!(object instanceof StackAnalyzedInfo)){
return;
}
StackAnalyzedInfo analyzedInfo = (StackAnalyzedInfo)object;
if(analyzedInfo.getExtension().equals(StackParser.UNIQUE_EXT)){
return;
}
StackFileInfo stackFileInfo = analyzedInfo.getStackFileInfo();
System.out.println("StackFile:"+ stackFileInfo.toString());
System.out.println("File:"+ analyzedInfo.toString());
String analyzedFilename = StackParser.getAnaylzedFilename(stackFileInfo.getFilename(), analyzedInfo.getExtension());
File file = new File(analyzedFilename);
if ( !file.isFile() )
return;
BufferedReader reader = null;
boolean isDetail = false;
boolean isStart = false;
try {
reader = new BufferedReader(new FileReader(file));
String line = null;
int totalCount = 0;
HashMap<String, Integer> map = new HashMap<String, Integer>();
while ( (line = reader.readLine()) != null ) {
line = line.trim();
if(line.length() > 0){
isStart = true;
}
if(line.length() == 0){
if(isStart){
isDetail = true;
}
continue;
}
if(isDetail){
totalCount++;
HtmlUtils.caculCounter(line, map);
}
}
ArrayList<ValueObject> list = HtmlUtils.sortCounter(map);
StringBuilder buffer = new StringBuilder(102400);
buffer.append(HtmlUtils.getMainBodyStart());
buffer.append(HtmlUtils.getCurrentConfigurationBody()).append("<br><br>");
buffer.append("<b>[ ").append(stackFileInfo.getFilename()).append(" ]</b><BR>");
buffer.append("<b>").append(analyzedInfo.toString()).append(" - ").append(analyzedFilename).append("</b><br><br>");
buffer.append("<table border='1'><tr align='center'><th>Count</th><th>Percent</th><th>Class.method</th></tr>");
int value;
for(ValueObject valueObject : list){
value = valueObject.getValue();
buffer.append("<tr><td align='right'>").append(value).append("</td><td align='right'>").append((int)((100 * value)/totalCount)).append('%').append("</td>");
buffer.append("<td align='left'>").append(valueObject.getKey()).append("</td></tr>");
}
buffer.append("</table>");
buffer.append(HtmlUtils.getMainBodyEnd());
displayContent(buffer.toString());
} catch ( Exception ex ) {
throw new RuntimeException(ex);
} finally {
try {
if ( reader != null )
reader.close();
} catch ( Exception ex ) {
}
}
}
private void createManualJob( FilterInputDialog.TASK jobtype, boolean isAscending ) {
try {
new FilterInputDialog(m_parentComposite.getShell(), isAscending, jobtype);
} catch ( Exception e ) {
throw new RuntimeException(e);
}
}
private void createAnalyzedPerformance( boolean isAscending ) {
createAnalyzedPerformance(getSelectedAnalyzedFunction(), isAscending);
}
public void createAnalyzedPerformance( String filter, boolean isAscending ) {
if ( filter == null )
return;
StackFileInfo stackFileInfo = getSelectedStackFileInfo();
if ( stackFileInfo != null ){
new PerformanceWindow(m_parentComposite.getShell(), stackFileInfo, filter, m_isExcludeStack, isAscending, m_isRemoveLine, m_isInerPercent);
}
}
public void analyzeFilterStack( String inputFilter ) {
analyzeFilterStack(inputFilter, true);
}
private String getSelectedAnalyzedFunction() {
Table table = getTable();
if(table == null){
return null;
}
TableItem [] items = table.getSelection();
if(items == null || items.length == 0){
return null;
}
return items[0].getText(3);
}
public void analyzeFilterStack( String inputFilter, boolean isInclude ) {
String filter = null;
if ( inputFilter == null ) {
filter = getSelectedAnalyzedFunction();
} else {
filter = inputFilter;
}
if ( filter == null )
return;
StackFileInfo stackFileInfo = getSelectedStackFileInfo();
if ( stackFileInfo == null )
return;
ParserConfigReader reader = new ParserConfigReader(stackFileInfo.getParserConfig().getConfigFilename());
ParserConfig config = reader.read();
StackFileInfo filteredStackFileInfo = processStackFile(StackParser.getWorkingThreadFilename(stackFileInfo.getFilename()), config, filter, false, isInclude);
addProcessedStack(filteredStackFileInfo);
}
private void viewThreadStack() {
viewThreadStack(getSelectedAnalyzedFunction());
}
public void viewThreadStack( String filter ) {
if ( filter == null )
return;
StackFileInfo stackFileInfo = getSelectedStackFileInfo();
if ( stackFileInfo == null )
return;
String filename = StackParser.getWorkingThreadFilename(stackFileInfo.getFilename());
int stackStartLine = stackFileInfo.getParserConfig().getStackStartLine();
if ( filename != null && filter != null ) {
Browser broswer = getBrowser();
if ( m_isExcludeStack )
broswer.setText(HtmlUtils.filterThreadStack(filename, filter, stackFileInfo.getParserConfig().getExcludeStack(), stackStartLine));
else
broswer.setText(HtmlUtils.filterThreadStack(filename, filter, null, stackStartLine));
}
}
private void viewServiceCall() {
viewServiceCall(getSelectedAnalyzedFunction());
}
public void viewServiceCall( String filter ) {
if ( filter == null )
return;
StackFileInfo stackFileInfo = getSelectedStackFileInfo();
if ( stackFileInfo == null )
return;
String filename = StackParser.getWorkingThreadFilename(stackFileInfo.getFilename());
int stackStartLine = stackFileInfo.getParserConfig().getStackStartLine();
if ( filename != null && filter != null ) {
getBrowser().setText(HtmlUtils.filterServiceCall(filename, filter, stackFileInfo.getParserConfig().getService(), stackStartLine));
}
}
private void copyFunctionName() {
String filter = getSelectedAnalyzedFunction();
if ( filter != null && filter.length() > 0 )
StringUtils.setClipboard(filter);
}
private void closeStackFileInfo() {
TreeItem item = getSelectedItemFromMainTree();
if(item == null){
return;
}
if(item.getData() instanceof StackAnalyzedInfo){
item = item.getParentItem();
}
item.removeAll();
item.dispose();
Table table = getTable();
table.clearAll();
table.setItemCount(0);
displayContent(null);
}
private void reanalyzeStackFileInfo() {
TreeItem item = getSelectedRootItemFromMainTree();
if(item == null){
return;
}
StackFileInfo stackFileInfo = (StackFileInfo)item.getData();
if ( stackFileInfo == null ){
return;
}
try {
ParserConfigReader reader = new ParserConfigReader(stackFileInfo.getParserConfig().getConfigFilename());
ParserConfig config = reader.read();
StackParser.removeAllAnalyzedFile(stackFileInfo);
processStackFile(stackFileInfo, config, null, false, true);
} catch ( RuntimeException ex ) {
closeStackFileInfo();
throw ex;
}
item.removeAll();
item.setText(stackFileInfo.toTreeInfo());
addMainTreeSubItem(item, stackFileInfo.getStackAnalyzedInfoList());
item.setExpanded(true);
clearTable();
}
public void openStackAnalyzer(){
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
try{
workbench.showPerspective(PerspectiveStackAnalyzer.ID, window);
window.getActivePage().showView(StackAnalyzerView.ID);
} catch (WorkbenchException e) {
System.out.println("Unable to open Perspective: " + PerspectiveStackAnalyzer.ID);
}
}
public void processStackFile(String fileName){
openStackAnalyzer();
File file = new File(fileName);
openFile(file, false);
}
public void processStackContents(String contents){
openStackAnalyzer();
openContents(contents);
}
}