/* * 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.views; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.draw2d.IFigure; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; import org.eclipse.zest.core.viewers.EntityConnectionData; import org.eclipse.zest.core.viewers.GraphViewer; import org.eclipse.zest.core.viewers.IEntityConnectionStyleProvider; import org.eclipse.zest.core.viewers.IEntityStyleProvider; import org.eclipse.zest.core.viewers.IGraphEntityContentProvider; import org.eclipse.zest.core.widgets.ZestStyles; import org.eclipse.zest.layouts.LayoutStyles; import org.eclipse.zest.layouts.algorithms.HorizontalTreeLayoutAlgorithm; import scouter.client.Images; import scouter.client.model.TextProxy; import scouter.client.net.INetReader; import scouter.client.net.TcpProxy; import scouter.client.popup.EditableMessageDialog; import scouter.client.popup.SQLFormatDialog; import scouter.client.server.ServerManager; import scouter.client.util.ColorUtil; import scouter.client.util.ConsoleProxy; import scouter.client.util.ExUtil; import scouter.client.util.ImageUtil; import scouter.client.util.TimeUtil; import scouter.client.xlog.actions.OpenXLogProfileJob; import scouter.io.DataInputX; import scouter.lang.CountryCode; import scouter.lang.pack.MapPack; import scouter.lang.pack.XLogPack; import scouter.lang.step.ApiCallStep; import scouter.lang.step.ApiCallSum; import scouter.lang.step.SqlStep; import scouter.lang.step.SqlSum; import scouter.lang.step.Step; import scouter.lang.step.StepEnum; import scouter.lang.step.ThreadSubmitStep; import scouter.lang.value.DecimalValue; import scouter.lang.value.MapValue; import scouter.net.RequestCmd; import scouter.util.FormatUtil; import scouter.util.HashUtil; import scouter.util.IPUtil; import scouter.util.LinkedMap; import scouter.util.LongEnumer; import scouter.util.LongKeyLinkedMap; import scouter.util.StringUtil; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; public class ObjectThreadDumpLockView extends ViewPart { public final static String ID = ObjectThreadDumpLockView.class.getName(); private GraphViewer viewer = null; private List<List<String>> lockList = new ArrayList<List<String>>(); private List<List<String>> threadList = new ArrayList<List<String>>(); private String date; boolean showSql = true, showApicall = true; public void createPartControl(Composite parent) { parent.setLayout(new FillLayout()); parent.addControlListener(new ControlListener() { public void controlResized(ControlEvent e) { viewer.applyLayout(); } public void controlMoved(ControlEvent e) { } }); viewer = new GraphViewer(parent, SWT.NONE); viewer.setContentProvider(new GraphContentProvider()); viewer.setLabelProvider(new GraphLabelProvider()); viewer.setLayoutAlgorithm(new HorizontalTreeLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING)); viewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { StructuredSelection sel = (StructuredSelection) event.getSelection(); Object o = sel.getFirstElement(); if (o instanceof DependencyElement) { DependencyElement de = (DependencyElement) o; switch (de.type) { case SERVICE: new OpenXLogProfileJob(ObjectThreadDumpLockView.this.getViewSite().getShell().getDisplay(), date, de.id, de.tags.getInt("serverId")).schedule(); break; case SQL: List<StyleRange> srList = new ArrayList<StyleRange>(); StringBuffer sb = new StringBuffer(); String sql = de.tags.getText("sql"); sb.append(sql); String error = null; if (de.error != 0) { error = TextProxy.error.getLoadText(date, de.error, de.tags.getInt("serverId")); } new SQLFormatDialog().show(sb.toString(), error); break; case API_CALL: srList = new ArrayList<StyleRange>(); sb = new StringBuffer(); String apicall = de.name; sb.append(apicall); if (de.error != 0) { error = TextProxy.error.getLoadText(date, de.error, de.tags.getInt("serverId")); if (StringUtil.isNotEmpty(error)) { sb.append("\n"); srList.add(new StyleRange(sb.length(), error.length(), ColorUtil.getInstance().getColor("red"), null, SWT.BOLD)); sb.append(error); } } new EditableMessageDialog().show("API Call", sb.toString(), srList); break; } } } }); IToolBarManager man = getViewSite().getActionBars().getToolBarManager(); Action sqlFilterAct = new Action("Show SQL", IAction.AS_CHECK_BOX) { public void run() { showSql = isChecked(); viewer.refresh(); viewer.applyLayout(); } }; sqlFilterAct.setImageDescriptor(ImageUtil.getImageDescriptor(Images.database)); man.add(sqlFilterAct); sqlFilterAct.setChecked(true); Action apiFilterAct = new Action("Show API Call", IAction.AS_CHECK_BOX) { public void run() { showApicall = isChecked(); viewer.refresh(); viewer.applyLayout(); } }; apiFilterAct.setImageDescriptor(ImageUtil.getImageDescriptor(Images.link)); man.add(apiFilterAct); apiFilterAct.setChecked(true); viewer.setFilters(new ViewerFilter[] {filter}); } public void loadStack(String stack, String partName) { this.setPartName("Lock - " + partName); parseStack(stack); checkLock(); /* this.date = date; this.setPartName("Service Flow - " + Hexa32.toString32(txid)); MapPack param = new MapPack(); param.put("date", date); param.put("txid", txid); new ProcessDependencyTask(RequestCmd.XLOG_READ_BY_TXID, param).schedule(); */ } private void parseStack(String stack){ BufferedReader reader = null; try { reader = new BufferedReader(new StringReader(stack)); String line; List<String> thread = null; while((line = reader.readLine()) != null){ line = line.trim(); if(line.indexOf("\"") >= 0 && line.indexOf("nid=") > 0){ thread = new ArrayList<String>(); threadList.add(thread); } if(line.length() > 0){ if(thread != null){ thread.add(line); } }else{ thread = null; } } }catch(Exception ex){ ex.printStackTrace(); }finally{ if(reader != null){ try{ reader.close();}catch(Exception ex){} } } } private void checkLock(){ if(threadList.size()==0){ return; } List<String> thread; String head; String object; String lockedObject = null; byte div; for(int i= 0; i < threadList.size(); i++){ thread = threadList.get(i); head = thread.get(0); div = 0; for(String string : thread){ if(string.indexOf("locked ") >= 0){ div = 1; lockedObject = getLockObject(string); } else if(string.indexOf("wait for ") >= 0){ div = 2; lockedObject = getLockObject(string); } else if(string.indexOf("waiting on ") >= 0){ div = 2; lockedObject = getLockObject(string); } if(div != 0){ } } } } private String getLockObject(String line){ String address = getString('<', '>', line); if(address == null){ return null; } String object = getString('(',')', line); if(object == null){ return address; } return address + "-" + object; } private String getString(char sChar, char eChar, String line){ int start, end; start = line.indexOf(sChar); if(start <0){ return null; } end = line.indexOf(eChar); if(end < 0 || start >= end){ return null; } return line.substring(start + 1, end); } class ProcessDependencyTask extends Job { final String requestCmd; final MapPack param; LongKeyLinkedMap<DependencyElement> rootMap = new LongKeyLinkedMap<DependencyElement>(); LongKeyLinkedMap<DependencyElement> serviceMap = new LongKeyLinkedMap<DependencyElement>(); public ProcessDependencyTask(String requestCmd, MapPack param) { super("Processing Dependencies Task"); this.requestCmd = requestCmd; this.param = param; } protected IStatus run(IProgressMonitor monitor) { monitor.beginTask("Collecting XLog/Profile....", IProgressMonitor.UNKNOWN); Iterator<Integer> itr = ServerManager.getInstance().getOpenServerList().iterator(); while (itr.hasNext()) { final int serverId = itr.next(); TcpProxy tcp = TcpProxy.getTcpProxy(serverId); try { tcp.process(requestCmd, param, new INetReader() { public void process(DataInputX in) throws IOException { XLogPack xlog = (XLogPack) in.readPack(); DependencyElement serviceElement = new DependencyElement(ElementType.SERVICE, xlog.txid); String objName = TextProxy.object.getLoadText(date, xlog.objHash, serverId); String serviceName = TextProxy.service.getLoadText(date, xlog.service, serverId); serviceElement.elapsed = xlog.elapsed; serviceElement.name = serviceName + "\n(" + objName + ")"; serviceElement.error = xlog.error; serviceElement.tags.put("caller", xlog.caller); serviceElement.tags.put("ip", IPUtil.toString(xlog.ipaddr)); serviceElement.tags.put("serverId", serverId); if (StringUtil.isNotEmpty(xlog.countryCode)) { serviceElement.tags.put("country", CountryCode.getCountryName(xlog.countryCode)); serviceElement.tags.put("city", TextProxy.city.getLoadText(date, xlog.city, serverId)); } serviceMap.put(xlog.txid, serviceElement); } }); } catch (Throwable th) { ConsoleProxy.errorSafe(th.toString()); } finally { TcpProxy.putTcpProxy(tcp); } } LongEnumer txidEnumer = serviceMap.keys(); while (txidEnumer.hasMoreElements()) { long txid = txidEnumer.nextLong(); final DependencyElement serviceElement = serviceMap.get(txid); final int serverId = (int) serviceElement.tags.getLong("serverId"); if (serverId == 0) continue; TcpProxy tcp = TcpProxy.getTcpProxy(serverId); try { MapPack param = new MapPack(); param.put("date", date); param.put("txid", new DecimalValue(txid)); tcp.process(RequestCmd.TRANX_PROFILE_FULL, param, new INetReader() { public void process(DataInputX in) throws IOException { byte[] buff = in.readBlob(); Step[] steps = Step.toObjects(buff); if (steps == null) return; for (Step step : steps) { stepToElement(serviceElement, step, serverId); } } }); } catch (Exception e) { ConsoleProxy.errorSafe(e.toString()); } finally { TcpProxy.putTcpProxy(tcp); } if (serviceMap.size() == 1 || serviceElement.tags.getLong("caller") == 0) { String ip = serviceElement.tags.getText("ip"); if (StringUtil.isNotEmpty(ip)) { int id = HashUtil.hash(ip); DependencyElement ipElement = rootMap.get(id); if (ipElement == null) { ipElement = new DependencyElement(ElementType.USER, id); if (StringUtil.isNotEmpty(serviceElement.tags.getText("country"))) { ipElement.name = ip + "\n(" + serviceElement.tags.getText("city") + ", " + serviceElement.tags.getText("country") + ")"; } else { ipElement.name = ip; } rootMap.put(id, ipElement); } ipElement.addChild(serviceElement); } else { long id = TimeUtil.getCurrentTime(); DependencyElement dummyElement = new DependencyElement(ElementType.USER, id); dummyElement.name = "???.???.???.???"; rootMap.put(id, dummyElement); } } } ExUtil.exec(viewer.getGraphControl(), new Runnable() { public void run() { viewer.setInput(rootMap); } }); return Status.OK_STATUS; } private void stepToElement(final DependencyElement serviceElement, Step step, final int serverId) { switch (step.getStepType()) { case StepEnum.APICALL: case StepEnum.APICALL2: ApiCallStep apicallstep = (ApiCallStep) step; DependencyElement apiElement = new DependencyElement(ElementType.API_CALL, apicallstep.txid + apicallstep.hash); apiElement.elapsed = apicallstep.elapsed; apiElement.error = apicallstep.error; apiElement.name = TextProxy.apicall.getLoadText(date, apicallstep.hash, serverId); apiElement.tags.put("serverId", serverId); if (apicallstep.txid != 0) { DependencyElement calledService = serviceMap.get(apicallstep.txid); if (calledService != null) { serviceElement.addChild(calledService); } else { serviceElement.addChild(apiElement); } } else { serviceElement.addChild(apiElement); } break; case StepEnum.APICALL_SUM: ApiCallSum apicallsum = (ApiCallSum) step; DependencyElement apiSumElement = new DependencyElement(ElementType.API_CALL, apicallsum.hash); apiSumElement.dupleCnt = apicallsum.count; apiSumElement.elapsed = (int) apicallsum.elapsed; apiSumElement.error = apicallsum.error; apiSumElement.name = TextProxy.apicall.getLoadText(date, apicallsum.hash, serverId); apiSumElement.tags.put("serverId", serverId); serviceElement.addChild(apiSumElement); break; case StepEnum.SQL: case StepEnum.SQL2: case StepEnum.SQL3: SqlStep sqlstep = (SqlStep) step; DependencyElement sqlElement = new DependencyElement(ElementType.SQL, sqlstep.hash); sqlElement.elapsed = sqlstep.elapsed; String table = TextProxy.sql_tables.getLoadText(date, sqlstep.hash, serverId); String sql = TextProxy.sql.getLoadText(date, sqlstep.hash, serverId).trim(); sqlElement.name = StringUtil.isNotEmpty(table) ? table : StringUtil.truncate(sql, 20) + "..."; sqlElement.name = table; sqlElement.error = sqlstep.error; sqlElement.tags.put("serverId", serverId); sqlElement.tags.put("sql", sql); serviceElement.addChild(sqlElement); break; case StepEnum.SQL_SUM: SqlSum sqlsum = (SqlSum) step; DependencyElement sqlSumElement = new DependencyElement(ElementType.SQL, sqlsum.hash); sqlSumElement.dupleCnt = sqlsum.count; sqlSumElement.elapsed = (int) sqlsum.elapsed; sqlSumElement.error = sqlsum.error; table = TextProxy.sql_tables.getLoadText(date, sqlsum.hash, serverId); sql = TextProxy.sql.getLoadText(date, sqlsum.hash, serverId).trim(); sqlSumElement.name = StringUtil.isNotEmpty(table) ? table : StringUtil.truncate(sql, 20) + "..."; sqlSumElement.tags.put("serverId", serverId); sqlSumElement.tags.put("sql", sql); serviceElement.addChild(sqlSumElement); break; case StepEnum.THREAD_SUBMIT: ThreadSubmitStep tsStep = (ThreadSubmitStep) step; TcpProxy tcp = TcpProxy.getTcpProxy(serverId); try { MapPack param = new MapPack(); param.put("date", date); param.put("txid", tsStep.txid); tcp.process(RequestCmd.TRANX_PROFILE_FULL, param, new INetReader() { public void process(DataInputX in) throws IOException { byte[] buff = in.readBlob(); Step[] steps = Step.toObjects(buff); if (steps == null) return; for (Step step : steps) { stepToElement(serviceElement, step, serverId); } } }); } catch (Throwable th) { ConsoleProxy.errorSafe(th.toString()); } finally { TcpProxy.putTcpProxy(tcp); } break; } } } public void setFocus() { } ViewerFilter filter = new ViewerFilter() { public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof DependencyElement) { DependencyElement de = (DependencyElement) element; switch (de.type) { case SQL: return showSql; case API_CALL: return showApicall; } } return true; } }; enum ElementType { USER, SERVICE, API_CALL, SQL } public static class DependencyElement { ElementType type; long id; int dupleCnt = 1; LinkedMap<Long, DependencyElement> childMap = new LinkedMap<Long, DependencyElement>(); public String name; public int elapsed; public int error; MapValue tags = new MapValue(); DependencyElement(ElementType type, long id) { this.type = type; this.id = id; } public void addChild(DependencyElement child) { DependencyElement obj = childMap.get(child.id); if (obj == null) { childMap.put(child.id, child); } else { obj.dupleCnt += child.dupleCnt; obj.elapsed += child.elapsed; if (child.error != 0) { obj.error = child.error; } } } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (id ^ (id >>> 32)); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DependencyElement other = (DependencyElement) obj; if (id != other.id) return false; return true; } } class GraphContentProvider implements IGraphEntityContentProvider { LongKeyLinkedMap<DependencyElement> root; public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput != null) { root = (LongKeyLinkedMap<DependencyElement>) newInput; } } public Object[] getElements(Object inputElement) { ArrayList<DependencyElement> list = new ArrayList<DependencyElement>(); LongEnumer ipEnumer = root.keys(); while (ipEnumer.hasMoreElements()) { long id = ipEnumer.nextLong(); DependencyElement ipElement = root.get(id); list.add(ipElement); collectElement(ipElement.childMap, list); } return list.toArray(new DependencyElement[list.size()]); } public Object[] getConnectedTo(Object entity) { if (entity instanceof DependencyElement) { ArrayList<DependencyElement> list = new ArrayList<DependencyElement>(); DependencyElement element = (DependencyElement) entity; Enumeration<DependencyElement> values = element.childMap.values(); while (values.hasMoreElements()) { DependencyElement de = values.nextElement(); list.add(de); } return list.toArray(new DependencyElement[list.size()]); } return null; } } private void collectElement(LinkedMap<Long, DependencyElement> childMap, ArrayList<DependencyElement> list) { Enumeration<DependencyElement> values = childMap.values(); while (values.hasMoreElements()) { DependencyElement element = values.nextElement(); list.add(element); collectElement(element.childMap, list); } } class GraphLabelProvider extends LabelProvider implements IEntityStyleProvider, IEntityConnectionStyleProvider { public String getText(Object element) { if (element instanceof DependencyElement) { DependencyElement de = (DependencyElement) element; if (de.name == null) de.name = ""; switch(de.type) { case SQL: case API_CALL: String name = de.name.trim().replaceAll("[\r\n]+", " ").replaceAll("\\s+", " "); if (name.length() > 50) { return name.substring(0, 40) + "..."; } return name; } return de.name; } else if (element instanceof EntityConnectionData) { if (((EntityConnectionData) element).dest instanceof DependencyElement) { DependencyElement de = (DependencyElement) ((EntityConnectionData) element).dest; String elapsed = FormatUtil.print(de.elapsed, "#,###") + " ms"; if (de.dupleCnt > 1) { return "(" + de.dupleCnt + ") " + elapsed; } else { return elapsed; } } } return null; } public Image getImage(Object element) { if (element instanceof DependencyElement) { DependencyElement de = (DependencyElement) element ; switch (de.type) { case USER: return Images.CONFIG_USER; case SERVICE: return Images.server; case API_CALL: return Images.link; case SQL : return Images.database; } } return null; } public Color getNodeHighlightColor(Object entity) { return ColorUtil.getInstance().getColor(SWT.COLOR_WHITE); } public Color getBorderColor(Object entity) { if (entity instanceof DependencyElement) { DependencyElement de = (DependencyElement) entity; if (de.type == ElementType.SERVICE) { return ColorUtil.getInstance().getColor(SWT.COLOR_DARK_BLUE); } else { return ColorUtil.getInstance().getColor(SWT.COLOR_WHITE); } } return null; } public Color getBorderHighlightColor(Object entity) { return null; } public int getBorderWidth(Object entity) { if (entity instanceof DependencyElement) { DependencyElement de = (DependencyElement) entity; if (de.type == ElementType.SERVICE) { return 1; } } return 0; } public Color getBackgroundColour(Object entity) { if (entity instanceof DependencyElement) { return ColorUtil.getInstance().getColor(SWT.COLOR_WHITE); } return null; } public Color getForegroundColour(Object entity) { return null; } public IFigure getTooltip(Object entity) { return null; } public boolean fisheyeNode(Object entity) { return false; } public int getConnectionStyle(Object src, Object dest) { return ZestStyles.CONNECTIONS_DIRECTED; } public Color getColor(Object src, Object dest) { if (dest instanceof DependencyElement) { DependencyElement de = (DependencyElement) dest; if (de.error != 0) { return ColorUtil.getInstance().getColor(SWT.COLOR_RED); } } return null; } public Color getHighlightColor(Object src, Object dest) { return null; } public int getLineWidth(Object src, Object dest) { return 0; } } }