1 package fi.vtt.simantics.procore.internal;
\r
4 import java.io.IOException;
\r
5 import java.lang.management.ManagementFactory;
\r
6 import java.lang.reflect.Method;
\r
7 import java.util.HashMap;
\r
8 import java.util.Map;
\r
9 import java.util.TreeSet;
\r
10 import java.util.UUID;
\r
12 import org.eclipse.core.runtime.Platform;
\r
13 import org.osgi.framework.Bundle;
\r
14 import org.simantics.databoard.binding.mutable.Variant;
\r
15 import org.simantics.databoard.parser.repository.DataValueRepository;
\r
16 import org.simantics.db.DirectStatements;
\r
17 import org.simantics.db.Resource;
\r
18 import org.simantics.db.Session;
\r
19 import org.simantics.db.Statement;
\r
20 import org.simantics.db.WriteGraph;
\r
21 import org.simantics.db.common.request.WriteRequest;
\r
22 import org.simantics.db.common.request.WriteResultRequest;
\r
23 import org.simantics.db.common.utils.Logger;
\r
24 import org.simantics.db.common.utils.NameUtils;
\r
25 import org.simantics.db.exception.DatabaseException;
\r
26 import org.simantics.db.exception.ServiceException;
\r
27 import org.simantics.db.exception.ValidationException;
\r
28 import org.simantics.db.impl.Activator;
\r
29 import org.simantics.db.impl.ClusterI;
\r
30 import org.simantics.db.impl.ResourceImpl;
\r
31 import org.simantics.db.impl.graph.ReadGraphImpl;
\r
32 import org.simantics.db.impl.graph.WriteLogger;
\r
33 import org.simantics.db.impl.query.QueryProcessor;
\r
34 import org.simantics.db.impl.query.QuerySupport;
\r
35 import org.simantics.db.procore.cluster.ClusterBig;
\r
36 import org.simantics.db.procore.cluster.ClusterImpl;
\r
37 import org.simantics.db.procore.cluster.ClusterSmall;
\r
38 import org.simantics.db.service.DebugSupport;
\r
39 import org.simantics.db.service.QueryControl;
\r
40 import org.simantics.db.service.XSupport;
\r
41 import org.simantics.layer0.Layer0;
\r
42 import org.simantics.scl.runtime.function.Function2;
\r
43 import org.simantics.scl.runtime.function.Function3;
\r
44 import org.simantics.scl.runtime.function.FunctionImpl2;
\r
45 import org.simantics.scl.runtime.function.FunctionImpl3;
\r
46 import org.simantics.utils.Development;
\r
47 import org.simantics.utils.FileUtils;
\r
49 import gnu.trove.map.hash.TIntIntHashMap;
\r
50 import gnu.trove.procedure.TIntIntProcedure;
\r
51 import gnu.trove.procedure.TIntShortProcedure;
\r
52 import gnu.trove.set.hash.TIntHashSet;
\r
54 public class DebugSupportImpl implements DebugSupport {
\r
56 final private Map<String, Function2<WriteGraph, String, Object>> getCommands = new HashMap<String, Function2<WriteGraph, String, Object>>();
\r
57 final private Map<String, Function3<WriteGraph, File, String, String>> listCommands = new HashMap<String, Function3<WriteGraph, File, String, String>>();
\r
58 final private Map<String, Function2<WriteGraph, String, String>> execCommands = new HashMap<String, Function2<WriteGraph, String, String>>();
\r
59 final private Map<String, Function2<WriteGraph, String, String>> printCommands = new HashMap<String, Function2<WriteGraph, String, String>>();
\r
61 private static SessionImplSocket getSession(WriteGraph graph) {
\r
62 return (SessionImplSocket)graph.getSession();
\r
65 DebugSupportImpl() {
\r
67 getCommands.put("listeners", new FunctionImpl2<WriteGraph, String, Object>() {
\r
70 public Object apply(WriteGraph graph, String args) {
\r
72 return getSession(graph).queryProvider2.getListenerReport();
\r
73 } catch (IOException e) {
\r
74 Logger.defaultLogError(e);
\r
75 return e.getMessage();
\r
81 listCommands.put("counters", new FunctionImpl3<WriteGraph, File, String, String>() {
\r
84 public String apply(WriteGraph graph, File file, String args) {
\r
86 return ReadGraphImpl.listCounters(file);
\r
87 } catch (IOException e) {
\r
88 Logger.defaultLogError(e);
\r
89 return e.getMessage();
\r
95 listCommands.put("queries", new FunctionImpl3<WriteGraph, File, String, String>() {
\r
98 public String apply(WriteGraph graph, File file, String args) {
\r
100 return getSession(graph).queryProvider2.reportQueries(file);
\r
101 } catch (IOException e) {
\r
102 Logger.defaultLogError(e);
\r
103 return e.getMessage();
\r
109 listCommands.put("queryActivity", new FunctionImpl3<WriteGraph, File, String, String>() {
\r
112 public String apply(WriteGraph graph, File file, String args) {
\r
114 return getSession(graph).queryProvider2.reportQueryActivity(file);
\r
115 } catch (IOException e) {
\r
116 Logger.defaultLogError(e);
\r
117 return e.getMessage();
\r
123 listCommands.put("listeners", new FunctionImpl3<WriteGraph, File, String, String>() {
\r
126 public String apply(WriteGraph graph, File file, String args) {
\r
128 return getSession(graph).queryProvider2.reportListeners(file);
\r
129 } catch (IOException e) {
\r
130 Logger.defaultLogError(e);
\r
131 return e.getMessage();
\r
137 listCommands.put("clusters", new FunctionImpl3<WriteGraph, File, String, String>() {
\r
140 public String apply(WriteGraph graph, File file, String args) {
\r
141 return reportClusters(getSession(graph), file);
\r
146 listCommands.put("cluster", new FunctionImpl3<WriteGraph, File, String, String>() {
\r
149 public String apply(WriteGraph graph, File file, String args) {
\r
150 return reportCluster(graph, file, args);
\r
155 listCommands.put("virtuals", new FunctionImpl3<WriteGraph, File, String, String>() {
\r
158 public String apply(WriteGraph graph, File file, String args) {
\r
159 return reportVirtuals(getSession(graph), file);
\r
164 listCommands.put("heap", new FunctionImpl3<WriteGraph, File, String, String>() {
\r
167 public String apply(WriteGraph graph, File file, String args) {
\r
173 Object bean = getBean();
\r
175 return "Could not retrieve bean.";
\r
177 Method m = bean.getClass().getMethod("dumpHeap", String.class, boolean.class);
\r
178 if (args.length() > 0) {
\r
179 m.invoke(bean, file.getParent() + "/" + args, true);
\r
181 m.invoke(bean, file.getAbsolutePath(), true);
\r
184 } catch (Throwable t) {
\r
185 Logger.defaultLogError(t);
\r
186 return "Unexpected exception " + t;
\r
189 return "Wrote " + file.getAbsolutePath();
\r
193 private Object getBean() {
\r
194 Class<?> beanClass = getBeanClass();
\r
195 if (beanClass == null)
\r
198 Object bean = ManagementFactory.newPlatformMXBeanProxy(
\r
199 ManagementFactory.getPlatformMBeanServer(),
\r
200 "com.sun.management:type=HotSpotDiagnostic",
\r
203 } catch (IOException e) {
\r
208 private Class<?> getBeanClass() {
\r
210 Class<?> clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
\r
212 } catch (ClassNotFoundException e) {
\r
219 execCommands.put("WriteLogger.read", new FunctionImpl2<WriteGraph, String, String>() {
\r
222 public String apply(WriteGraph graph, String args) {
\r
223 graph.getSession().async(new WriteRequest() {
\r
226 public void perform(WriteGraph graph) throws DatabaseException {
\r
228 WriteLogger.read(graph);
\r
229 } catch (Exception e) {
\r
230 e.printStackTrace();
\r
235 return "Started to read the write log.";
\r
240 execCommands.put("ReadGraph.resetCounters", new FunctionImpl2<WriteGraph, String, String>() {
\r
243 public String apply(WriteGraph graph, String args) {
\r
244 graph.getSession().async(new WriteRequest() {
\r
247 public void perform(WriteGraph graph) throws DatabaseException {
\r
249 ReadGraphImpl.resetCounters();
\r
250 } catch (Exception e) {
\r
251 e.printStackTrace();
\r
256 return "Started to read the write log.";
\r
261 execCommands.put("QueryControl.flush", new FunctionImpl2<WriteGraph, String, String>() {
\r
264 public String apply(WriteGraph graph, String args) {
\r
265 QueryControl qc = graph.getService(QueryControl.class);
\r
267 return "Flushed queries.";
\r
272 execCommands.put("DebugSupport.validateClusters", new FunctionImpl2<WriteGraph, String, String>() {
\r
275 public String apply(WriteGraph graph, String args) {
\r
276 return validateClusters(graph);
\r
281 execCommands.put("DebugSupport.writeForMs", new FunctionImpl2<WriteGraph, String, String>() {
\r
284 public String apply(WriteGraph graph, String args) {
\r
285 Integer amount = Integer.parseInt(args);
\r
286 return writeForMs(graph, amount);
\r
291 printCommands.put("nextId", new FunctionImpl2<WriteGraph, String, String>() {
\r
294 public String apply(WriteGraph graph, String args) {
\r
296 SessionImplSocket session = getSession(graph);
\r
297 ClusterImpl cluster = session.clusterTable.getNewResourceCluster(session.clusterTranslator, session.graphSession, false);
\r
298 long cid = cluster.getClusterId();
\r
299 int rid = cluster.getNumberOfResources(session.clusterTranslator);
\r
300 return cid + "#" + rid;
\r
301 } catch (Throwable t) {
\r
302 return UUID.randomUUID().toString();
\r
308 printCommands.put("usedMemory", new FunctionImpl2<WriteGraph, String, String>() {
\r
311 public String apply(WriteGraph graph, String args) {
\r
313 Runtime runtime = Runtime.getRuntime();
\r
314 return "" + (runtime.totalMemory() - runtime.freeMemory());
\r
315 } catch (Throwable t) {
\r
316 return UUID.randomUUID().toString();
\r
324 private String reportClusters(SessionImplSocket session, File file) {
\r
327 StringBuilder b = new StringBuilder();
\r
328 long totalApproxSize = 0;
\r
330 for(ClusterI cluster : session.clusterTable.getClusters()) {
\r
331 b.append("[" + cluster.getClusterKey() + "]: ");
\r
332 if(cluster instanceof ClusterSmall) b.append("ClusterSmall[" + cluster.getClusterId() + "]");
\r
333 if(cluster instanceof ClusterBig) b.append("ClusterBig[" + cluster.getClusterId() + "]");
\r
334 if(cluster instanceof ClusterWriteOnly) b.append("ClusterWriteOnly[" + cluster.getClusterId() + "]");
\r
335 if(cluster.isLoaded()) {
\r
336 long approx = cluster.getUsedSpace();
\r
337 b.append(" approx size = " + approx + " bytes.\n");
\r
338 totalApproxSize += approx;
\r
341 b.append(" is not loaded.\n");
\r
344 b.append("#Total approx size is " + totalApproxSize + " bytes.\n");
\r
345 b.append("#Amount of loaded clusters is " + loaded + ".\n");
\r
346 FileUtils.writeFile(file, b.toString().getBytes());
\r
347 } catch (IOException e) {
\r
348 e.printStackTrace();
\r
349 } catch (DatabaseException e) {
\r
350 e.printStackTrace();
\r
357 private String reportCluster(final WriteGraph graph, File file, String args) {
\r
361 final StringBuilder b = new StringBuilder();
\r
362 final SessionImplSocket session = (SessionImplSocket)graph.getSession();
\r
363 long clusterId = Long.parseLong(args);
\r
364 b.append("cluster id: " + clusterId);
\r
366 b.append("internal resources: ");
\r
368 ClusterI cluster = session.clusterTable.getClusterByClusterId(clusterId);
\r
369 for(int i=1;i<=cluster.getNumberOfResources(session.clusterTranslator);i++) {
\r
370 Resource r = session.getResource(i, clusterId);
\r
371 String def = NameUtils.getSafeName(graph, r);
\r
372 b.append( i+": " + def);
\r
375 if(cluster instanceof ClusterSmall) {
\r
376 ClusterSmall clusterSmall = (ClusterSmall)cluster;
\r
377 b.append("foreign resources: ");
\r
379 final TIntIntHashMap clusterHistogram = new TIntIntHashMap();
\r
380 clusterSmall.foreignTable.getResourceHashMap().forEachEntry(new TIntShortProcedure() {
\r
383 public boolean execute(int index, short pos) {
\r
385 Resource r = session.getResource(index);
\r
386 String def = NameUtils.getSafeName(graph, r, true);
\r
387 int cluster = index >>> 12;
\r
388 int key = index & 0xfff;
\r
389 int exist = clusterHistogram.get(cluster);
\r
390 clusterHistogram.put(cluster, exist+1);
\r
391 b.append( cluster + "$" + key +": " + def);
\r
393 } catch (ValidationException e) {
\r
394 e.printStackTrace();
\r
395 } catch (ServiceException e) {
\r
396 e.printStackTrace();
\r
401 b.append("foreign histogram: ");
\r
403 clusterHistogram.forEachEntry(new TIntIntProcedure() {
\r
406 public boolean execute(int cluster, int count) {
\r
407 b.append( cluster +": " + count);
\r
414 FileUtils.writeFile(file, b.toString().getBytes());
\r
415 } catch (IOException e) {
\r
416 e.printStackTrace();
\r
417 } catch (DatabaseException e) {
\r
418 e.printStackTrace();
\r
425 private String reportVirtuals(SessionImplSocket session, File file) {
\r
427 session.virtualGraphServerSupport.report(file);
\r
433 public Object query(Session session, final String command) {
\r
435 return session.sync(new WriteResultRequest<Object>() {
\r
438 public Object perform(WriteGraph graph) throws DatabaseException {
\r
439 return query(graph, command);
\r
443 } catch (DatabaseException e) {
\r
444 Logger.defaultLogError(e);
\r
450 public Object query(WriteGraph graph, String command) {
\r
452 Bundle bundle = Platform.getBundle(Activator.BUNDLE_ID);
\r
453 File base = Utils.getBaseFile(bundle);
\r
455 command = command.trim();
\r
459 if("help".equals(command)) {
\r
462 "Welcome to the Simantics session debugger.<br><br>" +
\r
463 "This shell allows you to make following queries into the running Simantics database session:<br>" +
\r
464 "<ul><li>Get commands, which return debug objects. Type 'help get' to obtain more information.</li>" +
\r
465 "<li>List commands, which create debug listings into files. Type 'help list' to obtain more information.</li>" +
\r
466 "<li>Print commands, which output information about session state. Type 'help print' to obtain more information.</li>" +
\r
467 "<li>Set commands, which modify session state variables. Type 'help set' to obtain more information.</li>" +
\r
468 "<li>Exec commands, which perform certain actions. Type 'help exec' to obtain more information.</li></ul>"
\r
471 } else if ("help get".equals(command)) {
\r
473 StringBuilder b = new StringBuilder();
\r
474 b.append("The following get commands are available.<br><ul>");
\r
475 for(String key : getCommands.keySet())
\r
476 b.append("<li>" + key + "</li>");
\r
480 return b.toString();
\r
482 } else if ("help list".equals(command)) {
\r
484 StringBuilder b = new StringBuilder();
\r
485 b.append("The following list commands are available.<br><ul>");
\r
486 for(String key : listCommands.keySet())
\r
487 b.append("<li>" + key + "</li>");
\r
491 return b.toString();
\r
493 } else if ("help exec".equals(command)) {
\r
495 StringBuilder b = new StringBuilder();
\r
496 b.append("The following exec commands are available.<br><ul>");
\r
497 for(String key : execCommands.keySet())
\r
498 b.append("<li>" + key + "</li>");
\r
502 return b.toString();
\r
504 } else if ("help print".equals(command)) {
\r
506 StringBuilder b = new StringBuilder();
\r
507 b.append("The following print commands are available.<br><ul>");
\r
508 for(String key : printCommands.keySet())
\r
509 b.append("<li>" + key + "</li>");
\r
513 return b.toString();
\r
515 } else if ("help set".equals(command)) {
\r
517 StringBuilder b = new StringBuilder();
\r
518 b.append("The following set commands are available.<br><ul>");
\r
520 for(Map.Entry<String, Variant> e : Development.getProperties().entrySet()) {
\r
521 b.append("<li>" + e.getKey() + " - " + e.getValue().getBinding().type() + "</li>");
\r
526 return b.toString();
\r
528 } else if(command.startsWith("get")) {
\r
530 String remainder = command.substring(3).trim();
\r
532 for(Map.Entry<String, Function2<WriteGraph, String, Object>> e : getCommands.entrySet()) {
\r
533 String key = e.getKey();
\r
534 if(remainder.startsWith(key)) {
\r
535 String args = remainder.substring(key.length()).trim();
\r
536 return e.getValue().apply(graph, args);
\r
540 } else if(command.startsWith("list")) {
\r
542 String remainder = command.substring(4).trim();
\r
544 for(Map.Entry<String, Function3<WriteGraph, File, String, String>> e : listCommands.entrySet()) {
\r
545 String key = e.getKey();
\r
546 if(remainder.startsWith(key)) {
\r
547 String args = remainder.substring(key.length()).trim();
\r
548 File file = new File(base, key + ".list");
\r
550 e.getValue().apply(graph, file, args);
\r
551 return "Wrote " + file.getAbsolutePath();
\r
555 } else if(command.startsWith("set")) {
\r
557 String remainder = command.substring(3).trim();
\r
558 String[] keyAndValue = remainder.split("=");
\r
559 if(keyAndValue.length == 2) {
\r
561 Variant exist = Development.getProperties().get(keyAndValue[0]);
\r
562 if(exist != null) {
\r
563 Development.setProperty(keyAndValue[0], exist.getBinding().parseValue(keyAndValue[1], new DataValueRepository()), exist.getBinding());
\r
564 return "Property " + keyAndValue[0] + " was set to '" + keyAndValue[1] + "'";
\r
566 return query(graph, "help set");
\r
569 return query(graph, "help set");
\r
572 } else if(command.startsWith("print")) {
\r
574 String remainder = command.substring(5).trim();
\r
576 for(Map.Entry<String, Function2<WriteGraph, String, String>> e : printCommands.entrySet()) {
\r
577 String key = e.getKey();
\r
578 if(remainder.startsWith(key)) {
\r
579 String args = remainder.substring(key.length()).trim();
\r
580 return e.getValue().apply(graph, args);
\r
584 } else if(command.startsWith("exec")) {
\r
586 String remainder = command.substring(4).trim();
\r
588 for(Map.Entry<String, Function2<WriteGraph, String, String>> e : execCommands.entrySet()) {
\r
589 String key = e.getKey();
\r
590 if(remainder.startsWith(key)) {
\r
591 String args = remainder.substring(key.length()).trim();
\r
592 return e.getValue().apply(graph, args);
\r
598 return "Unknown command '" + command + "'";
\r
600 } catch (Throwable t) {
\r
602 t.printStackTrace();
\r
604 return t.getMessage();
\r
609 private void writeResouce(WriteGraph graph) throws DatabaseException {
\r
610 Layer0 l0 = Layer0.getInstance(graph);
\r
611 Resource r = graph.newResource();
\r
612 graph.claim(r, l0.InstanceOf, l0.Entity);
\r
613 XSupport xs = (XSupport)graph.getService(XSupport.class);
\r
614 xs.flushCluster(r);
\r
616 private void wait(int amount) {
\r
617 long start = System.nanoTime();
\r
618 long limit = amount * 1000000L;
\r
619 while ((System.nanoTime() - start) < limit) {
\r
622 } catch (InterruptedException e) {
\r
623 e.printStackTrace();
\r
627 String writeForMs(WriteGraph graph, int amount) {
\r
629 writeResouce(graph);
\r
631 writeResouce(graph);
\r
632 } catch (DatabaseException e) {
\r
633 e.printStackTrace();
\r
636 return "Slept for " + amount + " ms in write transaction.";
\r
639 String validateClusters(WriteGraph graph) {
\r
641 ReadGraphImpl impl = (ReadGraphImpl)graph;
\r
642 QueryProcessor processor = impl.processor;
\r
644 QuerySupport qs = graph.getService(QuerySupport.class);
\r
646 TIntHashSet done = new TIntHashSet();
\r
647 TreeSet<Integer> fringe = new TreeSet<Integer>();
\r
649 ResourceImpl root = (ResourceImpl)graph.getRootLibrary();
\r
652 fringe.add(root.id);
\r
654 while(!fringe.isEmpty()) {
\r
655 int r = fringe.first();
\r
657 DirectStatements ds = qs.getStatements(impl, r, processor, true);
\r
658 for(Statement stm : ds) {
\r
659 ResourceImpl p = (ResourceImpl)stm.getPredicate();
\r
660 ResourceImpl o = (ResourceImpl)stm.getObject();
\r
661 if(done.add(p.id)) fringe.add(p.id);
\r
662 if(done.add(o.id)) fringe.add(o.id);
\r
664 qs.getValue(impl, r);
\r
667 return "Validated " + done.size() + " resources.";
\r