1 package fi.vtt.simantics.procore.internal;
4 import java.io.IOException;
5 import java.lang.management.ManagementFactory;
6 import java.lang.reflect.Method;
7 import java.util.HashMap;
9 import java.util.TreeSet;
10 import java.util.UUID;
12 import org.eclipse.core.runtime.Platform;
13 import org.osgi.framework.Bundle;
14 import org.simantics.databoard.binding.mutable.Variant;
15 import org.simantics.databoard.parser.repository.DataValueRepository;
16 import org.simantics.db.DirectStatements;
17 import org.simantics.db.Resource;
18 import org.simantics.db.Session;
19 import org.simantics.db.Statement;
20 import org.simantics.db.WriteGraph;
21 import org.simantics.db.common.request.WriteRequest;
22 import org.simantics.db.common.request.WriteResultRequest;
23 import org.simantics.db.common.utils.Logger;
24 import org.simantics.db.common.utils.NameUtils;
25 import org.simantics.db.exception.DatabaseException;
26 import org.simantics.db.exception.ServiceException;
27 import org.simantics.db.exception.ValidationException;
28 import org.simantics.db.impl.Activator;
29 import org.simantics.db.impl.ClusterI;
30 import org.simantics.db.impl.ResourceImpl;
31 import org.simantics.db.impl.graph.ReadGraphImpl;
32 import org.simantics.db.impl.graph.WriteLogger;
33 import org.simantics.db.impl.query.QueryProcessor;
34 import org.simantics.db.impl.query.QuerySupport;
35 import org.simantics.db.procore.cluster.ClusterBig;
36 import org.simantics.db.procore.cluster.ClusterImpl;
37 import org.simantics.db.procore.cluster.ClusterSmall;
38 import org.simantics.db.service.DebugSupport;
39 import org.simantics.db.service.QueryControl;
40 import org.simantics.db.service.XSupport;
41 import org.simantics.layer0.Layer0;
42 import org.simantics.scl.runtime.function.Function2;
43 import org.simantics.scl.runtime.function.Function3;
44 import org.simantics.scl.runtime.function.FunctionImpl2;
45 import org.simantics.scl.runtime.function.FunctionImpl3;
46 import org.simantics.utils.Development;
47 import org.simantics.utils.FileUtils;
48 import org.slf4j.LoggerFactory;
50 import gnu.trove.map.hash.TIntIntHashMap;
51 import gnu.trove.procedure.TIntIntProcedure;
52 import gnu.trove.procedure.TIntShortProcedure;
53 import gnu.trove.set.hash.TIntHashSet;
55 public class DebugSupportImpl implements DebugSupport {
57 private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DebugSupportImpl.class);
59 final private Map<String, Function2<WriteGraph, String, Object>> getCommands = new HashMap<String, Function2<WriteGraph, String, Object>>();
60 final private Map<String, Function3<WriteGraph, File, String, String>> listCommands = new HashMap<String, Function3<WriteGraph, File, String, String>>();
61 final private Map<String, Function2<WriteGraph, String, String>> execCommands = new HashMap<String, Function2<WriteGraph, String, String>>();
62 final private Map<String, Function2<WriteGraph, String, String>> printCommands = new HashMap<String, Function2<WriteGraph, String, String>>();
64 private static SessionImplSocket getSession(WriteGraph graph) {
65 return (SessionImplSocket)graph.getSession();
70 getCommands.put("listeners", new FunctionImpl2<WriteGraph, String, Object>() {
73 public Object apply(WriteGraph graph, String args) {
75 return getSession(graph).queryProvider2.listening.getListenerReport();
76 } catch (IOException e) {
77 Logger.defaultLogError(e);
78 return e.getMessage();
84 listCommands.put("counters", new FunctionImpl3<WriteGraph, File, String, String>() {
87 public String apply(WriteGraph graph, File file, String args) {
89 return ReadGraphImpl.listCounters(file);
90 } catch (IOException e) {
91 Logger.defaultLogError(e);
92 return e.getMessage();
98 listCommands.put("queries", new FunctionImpl3<WriteGraph, File, String, String>() {
101 public String apply(WriteGraph graph, File file, String args) {
103 return getSession(graph).queryProvider2.reportQueries(file);
104 } catch (IOException e) {
105 Logger.defaultLogError(e);
106 return e.getMessage();
112 listCommands.put("queryData", new FunctionImpl3<WriteGraph, File, String, String>() {
115 public String apply(WriteGraph graph, File file, String args) {
117 getSession(graph).queryProvider2.save();
118 return "Saved queries";
119 } catch (IOException e) {
120 LOGGER.error("Error while saving queries", e);
121 return e.getMessage();
127 listCommands.put("queryActivity", new FunctionImpl3<WriteGraph, File, String, String>() {
130 public String apply(WriteGraph graph, File file, String args) {
132 return getSession(graph).queryProvider2.reportQueryActivity(file);
133 } catch (IOException e) {
134 Logger.defaultLogError(e);
135 return e.getMessage();
141 listCommands.put("listeners", new FunctionImpl3<WriteGraph, File, String, String>() {
144 public String apply(WriteGraph graph, File file, String args) {
146 return getSession(graph).queryProvider2.listening.reportListeners(file);
147 } catch (IOException e) {
148 Logger.defaultLogError(e);
149 return e.getMessage();
155 listCommands.put("clusters", new FunctionImpl3<WriteGraph, File, String, String>() {
158 public String apply(WriteGraph graph, File file, String args) {
159 return reportClusters(getSession(graph), file);
164 listCommands.put("cluster", new FunctionImpl3<WriteGraph, File, String, String>() {
167 public String apply(WriteGraph graph, File file, String args) {
168 return reportCluster(graph, file, args);
173 listCommands.put("virtuals", new FunctionImpl3<WriteGraph, File, String, String>() {
176 public String apply(WriteGraph graph, File file, String args) {
177 return reportVirtuals(getSession(graph), file);
182 listCommands.put("heap", new FunctionImpl3<WriteGraph, File, String, String>() {
185 public String apply(WriteGraph graph, File file, String args) {
191 Object bean = getBean();
193 return "Could not retrieve bean.";
195 Method m = bean.getClass().getMethod("dumpHeap", String.class, boolean.class);
196 if (args.length() > 0) {
197 m.invoke(bean, file.getParent() + "/" + args, true);
199 m.invoke(bean, file.getAbsolutePath(), true);
202 } catch (Throwable t) {
203 Logger.defaultLogError(t);
204 return "Unexpected exception " + t;
207 return "Wrote " + file.getAbsolutePath();
211 private Object getBean() {
212 Class<?> beanClass = getBeanClass();
213 if (beanClass == null)
216 Object bean = ManagementFactory.newPlatformMXBeanProxy(
217 ManagementFactory.getPlatformMBeanServer(),
218 "com.sun.management:type=HotSpotDiagnostic",
221 } catch (IOException e) {
226 private Class<?> getBeanClass() {
228 Class<?> clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
230 } catch (ClassNotFoundException e) {
237 execCommands.put("WriteLogger.read", new FunctionImpl2<WriteGraph, String, String>() {
240 public String apply(WriteGraph graph, String args) {
241 graph.getSession().async(new WriteRequest() {
244 public void perform(WriteGraph graph) throws DatabaseException {
246 WriteLogger.read(graph);
247 } catch (Exception e) {
253 return "Started to read the write log.";
258 execCommands.put("ReadGraph.resetCounters", new FunctionImpl2<WriteGraph, String, String>() {
261 public String apply(WriteGraph graph, String args) {
262 graph.getSession().async(new WriteRequest() {
265 public void perform(WriteGraph graph) throws DatabaseException {
267 ReadGraphImpl.resetCounters();
268 } catch (Exception e) {
274 return "Started to read the write log.";
279 execCommands.put("QueryControl.flush", new FunctionImpl2<WriteGraph, String, String>() {
282 public String apply(WriteGraph graph, String args) {
283 QueryControl qc = graph.getService(QueryControl.class);
285 return "Flushed queries.";
290 execCommands.put("DebugSupport.validateClusters", new FunctionImpl2<WriteGraph, String, String>() {
293 public String apply(WriteGraph graph, String args) {
294 return validateClusters(graph);
299 execCommands.put("DebugSupport.writeForMs", new FunctionImpl2<WriteGraph, String, String>() {
302 public String apply(WriteGraph graph, String args) {
303 Integer amount = Integer.parseInt(args);
304 return writeForMs(graph, amount);
309 printCommands.put("nextId", new FunctionImpl2<WriteGraph, String, String>() {
312 public String apply(WriteGraph graph, String args) {
314 SessionImplSocket session = getSession(graph);
315 ClusterImpl cluster = session.clusterTable.getNewResourceCluster(session.clusterTranslator, session.graphSession, false);
316 long cid = cluster.getClusterId();
317 int rid = cluster.getNumberOfResources(session.clusterTranslator);
318 return cid + "#" + rid;
319 } catch (Throwable t) {
320 return UUID.randomUUID().toString();
326 printCommands.put("usedMemory", new FunctionImpl2<WriteGraph, String, String>() {
329 public String apply(WriteGraph graph, String args) {
331 Runtime runtime = Runtime.getRuntime();
332 return "" + (runtime.totalMemory() - runtime.freeMemory());
333 } catch (Throwable t) {
334 return UUID.randomUUID().toString();
342 private String reportClusters(SessionImplSocket session, File file) {
345 StringBuilder b = new StringBuilder();
346 long totalApproxSize = 0;
348 for(ClusterI cluster : session.clusterTable.getClusters()) {
349 b.append("[" + cluster.getClusterKey() + "]: ");
350 if(cluster instanceof ClusterSmall) b.append("ClusterSmall[" + cluster.getClusterId() + "]");
351 if(cluster instanceof ClusterBig) b.append("ClusterBig[" + cluster.getClusterId() + "]");
352 if(cluster instanceof ClusterWriteOnly) b.append("ClusterWriteOnly[" + cluster.getClusterId() + "]");
353 if(cluster.isLoaded()) {
354 long approx = cluster.getUsedSpace();
355 b.append(" approx size = " + approx + " bytes.\n");
356 totalApproxSize += approx;
359 b.append(" is not loaded.\n");
362 b.append("#Total approx size is " + totalApproxSize + " bytes.\n");
363 b.append("#Amount of loaded clusters is " + loaded + ".\n");
364 FileUtils.writeFile(file, b.toString().getBytes());
365 } catch (IOException e) {
367 } catch (DatabaseException e) {
375 private String reportCluster(final WriteGraph graph, File file, String args) {
379 final StringBuilder b = new StringBuilder();
380 final SessionImplSocket session = (SessionImplSocket)graph.getSession();
381 long clusterId = Long.parseLong(args);
382 b.append("cluster id: " + clusterId);
384 b.append("internal resources: ");
386 ClusterI cluster = session.clusterTable.getClusterByClusterId(clusterId);
387 for(int i=1;i<=cluster.getNumberOfResources(session.clusterTranslator);i++) {
388 Resource r = session.getResource(i, clusterId);
389 String def = NameUtils.getSafeName(graph, r);
390 b.append( i+": " + def);
393 if(cluster instanceof ClusterSmall) {
394 ClusterSmall clusterSmall = (ClusterSmall)cluster;
395 b.append("foreign resources: ");
397 final TIntIntHashMap clusterHistogram = new TIntIntHashMap();
398 clusterSmall.foreignTable.getResourceHashMap().forEachEntry(new TIntShortProcedure() {
401 public boolean execute(int index, short pos) {
403 Resource r = session.getResource(index);
404 String def = NameUtils.getSafeName(graph, r, true);
405 int cluster = index >>> 12;
406 int key = index & 0xfff;
407 int exist = clusterHistogram.get(cluster);
408 clusterHistogram.put(cluster, exist+1);
409 b.append( cluster + "$" + key +": " + def);
411 } catch (ValidationException e) {
413 } catch (ServiceException e) {
419 b.append("foreign histogram: ");
421 clusterHistogram.forEachEntry(new TIntIntProcedure() {
424 public boolean execute(int cluster, int count) {
425 b.append( cluster +": " + count);
432 FileUtils.writeFile(file, b.toString().getBytes());
433 } catch (IOException e) {
435 } catch (DatabaseException e) {
443 private String reportVirtuals(SessionImplSocket session, File file) {
445 session.virtualGraphServerSupport.report(file);
451 public Object query(Session session, final String command) {
453 return session.sync(new WriteResultRequest<Object>() {
456 public Object perform(WriteGraph graph) throws DatabaseException {
457 return query(graph, command);
461 } catch (DatabaseException e) {
462 Logger.defaultLogError(e);
468 public Object query(WriteGraph graph, String command) {
470 Bundle bundle = Platform.getBundle(Activator.BUNDLE_ID);
471 File base = Utils.getBaseFile(bundle);
473 command = command.trim();
477 if("help".equals(command)) {
480 "Welcome to the Simantics session debugger.<br><br>" +
481 "This shell allows you to make following queries into the running Simantics database session:<br>" +
482 "<ul><li>Get commands, which return debug objects. Type 'help get' to obtain more information.</li>" +
483 "<li>List commands, which create debug listings into files. Type 'help list' to obtain more information.</li>" +
484 "<li>Print commands, which output information about session state. Type 'help print' to obtain more information.</li>" +
485 "<li>Set commands, which modify session state variables. Type 'help set' to obtain more information.</li>" +
486 "<li>Exec commands, which perform certain actions. Type 'help exec' to obtain more information.</li></ul>"
489 } else if ("help get".equals(command)) {
491 StringBuilder b = new StringBuilder();
492 b.append("The following get commands are available.<br><ul>");
493 for(String key : getCommands.keySet())
494 b.append("<li>" + key + "</li>");
500 } else if ("help list".equals(command)) {
502 StringBuilder b = new StringBuilder();
503 b.append("The following list commands are available.<br><ul>");
504 for(String key : listCommands.keySet())
505 b.append("<li>" + key + "</li>");
511 } else if ("help exec".equals(command)) {
513 StringBuilder b = new StringBuilder();
514 b.append("The following exec commands are available.<br><ul>");
515 for(String key : execCommands.keySet())
516 b.append("<li>" + key + "</li>");
522 } else if ("help print".equals(command)) {
524 StringBuilder b = new StringBuilder();
525 b.append("The following print commands are available.<br><ul>");
526 for(String key : printCommands.keySet())
527 b.append("<li>" + key + "</li>");
533 } else if ("help set".equals(command)) {
535 StringBuilder b = new StringBuilder();
536 b.append("The following set commands are available.<br><ul>");
538 for(Map.Entry<String, Variant> e : Development.getProperties().entrySet()) {
539 b.append("<li>" + e.getKey() + " - " + e.getValue().getBinding().type() + "</li>");
546 } else if(command.startsWith("get")) {
548 String remainder = command.substring(3).trim();
550 for(Map.Entry<String, Function2<WriteGraph, String, Object>> e : getCommands.entrySet()) {
551 String key = e.getKey();
552 if(remainder.startsWith(key)) {
553 String args = remainder.substring(key.length()).trim();
554 return e.getValue().apply(graph, args);
558 } else if(command.startsWith("list")) {
560 String remainder = command.substring(4).trim();
562 for(Map.Entry<String, Function3<WriteGraph, File, String, String>> e : listCommands.entrySet()) {
563 String key = e.getKey();
564 if(remainder.startsWith(key)) {
565 String args = remainder.substring(key.length()).trim();
566 File file = new File(base, key + ".list");
568 e.getValue().apply(graph, file, args);
569 return "Wrote " + file.getAbsolutePath();
573 } else if(command.startsWith("set")) {
575 String remainder = command.substring(3).trim();
576 String[] keyAndValue = remainder.split("=");
577 if(keyAndValue.length == 2) {
579 Variant exist = Development.getProperties().get(keyAndValue[0]);
581 Development.setProperty(keyAndValue[0], exist.getBinding().parseValue(keyAndValue[1], new DataValueRepository()), exist.getBinding());
582 return "Property " + keyAndValue[0] + " was set to '" + keyAndValue[1] + "'";
584 return query(graph, "help set");
587 return query(graph, "help set");
590 } else if(command.startsWith("print")) {
592 String remainder = command.substring(5).trim();
594 for(Map.Entry<String, Function2<WriteGraph, String, String>> e : printCommands.entrySet()) {
595 String key = e.getKey();
596 if(remainder.startsWith(key)) {
597 String args = remainder.substring(key.length()).trim();
598 return e.getValue().apply(graph, args);
602 } else if(command.startsWith("exec")) {
604 String remainder = command.substring(4).trim();
606 for(Map.Entry<String, Function2<WriteGraph, String, String>> e : execCommands.entrySet()) {
607 String key = e.getKey();
608 if(remainder.startsWith(key)) {
609 String args = remainder.substring(key.length()).trim();
610 return e.getValue().apply(graph, args);
616 return "Unknown command '" + command + "'";
618 } catch (Throwable t) {
622 return t.getMessage();
627 private void writeResouce(WriteGraph graph) throws DatabaseException {
628 Layer0 l0 = Layer0.getInstance(graph);
629 Resource r = graph.newResource();
630 graph.claim(r, l0.InstanceOf, l0.Entity);
631 XSupport xs = (XSupport)graph.getService(XSupport.class);
634 private void wait(int amount) {
635 long start = System.nanoTime();
636 long limit = amount * 1000000L;
637 while ((System.nanoTime() - start) < limit) {
640 } catch (InterruptedException e) {
645 String writeForMs(WriteGraph graph, int amount) {
650 } catch (DatabaseException e) {
654 return "Slept for " + amount + " ms in write transaction.";
657 String validateClusters(WriteGraph graph) {
659 ReadGraphImpl impl = (ReadGraphImpl)graph;
660 QueryProcessor processor = impl.processor;
662 QuerySupport qs = graph.getService(QuerySupport.class);
664 TIntHashSet done = new TIntHashSet();
665 TreeSet<Integer> fringe = new TreeSet<Integer>();
667 ResourceImpl root = (ResourceImpl)graph.getRootLibrary();
672 while(!fringe.isEmpty()) {
673 int r = fringe.first();
675 DirectStatements ds = qs.getStatements(impl, r, processor, true);
676 for(Statement stm : ds) {
677 ResourceImpl p = (ResourceImpl)stm.getPredicate();
678 ResourceImpl o = (ResourceImpl)stm.getObject();
679 if(done.add(p.id)) fringe.add(p.id);
680 if(done.add(o.id)) fringe.add(o.id);
682 qs.getValue(impl, r);
685 return "Validated " + done.size() + " resources.";