SRADA-215: implemented tracing management and start command
authorVladislav Eliseev <v.eliseev@samsung.com>
Fri, 22 Apr 2016 13:56:33 +0000 (16:56 +0300)
committerVladislav Eliseev <v.eliseev@samsung.com>
Fri, 22 Apr 2016 13:57:39 +0000 (16:57 +0300)
Now ProcessManager offers high level interface to start and stop
tracing processes and refresh and answer their states.

Change-Id: I678597b1316fb0fdf82ab57aa845eb3c3ea5e09a

org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/CliInternals.java
org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/commands/StartCommand.java
org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManager.java
org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerMBean.java
org.tizen.dynamicanalyzer.cli/test/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerTest.java [new file with mode: 0644]

index 6c195b6..caccc3e 100644 (file)
@@ -1,17 +1,24 @@
 package org.tizen.dynamicanalyzer.cli;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.tizen.dynamicanalyzer.cli.manager.ClientUtils;
+import org.tizen.dynamicanalyzer.cli.manager.ProcessManagerMBean;
+import org.tizen.dynamicanalyzer.cli.tracing.TracingArguments;
 import org.tizen.dynamicanalyzer.common.AnalyzerConstants;
+import org.tizen.dynamicanalyzer.common.DAResult;
+import org.tizen.dynamicanalyzer.common.DAResult.ErrorCode;
 import org.tizen.dynamicanalyzer.communicator.DACommunicator;
 import org.tizen.dynamicanalyzer.communicator.DeviceInfo;
 import org.tizen.dynamicanalyzer.communicator.DeviceManager;
 import org.tizen.dynamicanalyzer.project.PackageInfo;
 import org.tizen.dynamicanalyzer.setting.SettingDataManager;
 import org.tizen.dynamicanalyzer.setting.Template;
+import org.tizen.dynamicanalyzer.util.Logger;
 import org.tizen.sdblib.IDevice;
 
 public final class CliInternals {
@@ -167,4 +174,28 @@ public final class CliInternals {
 
                return packages;
        }
+
+       /**
+        * Start tracing with specified tracing parameters.
+        * This method is not blocking but requires some time to establish connection.
+        *
+        * @param args tracing parameters
+        * @return result of async start tracing task
+        * @throws InterruptedException if internal call was interrupted
+        */
+       public static DAResult startTracing(TracingArguments args) throws InterruptedException {
+               ProcessManagerMBean pmProxy = null;
+
+               try {
+                       pmProxy = ClientUtils.getProcessManagerProxy(true);
+               } catch (IOException e) {
+                       Logger.error("Error while communicating with proccess manager: %s.", e.toString());
+                       return new DAResult(ErrorCode.ERR_EXCEPTION_OCCURRED, "Can't connect to the tracing process manager.");
+               } catch (InterruptedException e) {
+                       Logger.warning("Communication with process manager was interrupted.");
+                       throw e;
+               }
+
+               return pmProxy.startTracing(args);
+       }
 }
index 8810f42..f658525 100644 (file)
@@ -2,9 +2,11 @@ package org.tizen.dynamicanalyzer.cli.commands;
 
 import java.io.IOException;
 
-import org.tizen.dynamicanalyzer.cli.manager.ClientUtils;
-import org.tizen.dynamicanalyzer.cli.manager.ProcessManagerMBean;
+import org.apache.commons.cli.ParseException;
+import org.tizen.dynamicanalyzer.cli.CliInternals;
+import org.tizen.dynamicanalyzer.cli.tracing.TracingArguments;
 import org.tizen.dynamicanalyzer.cli.tracing.TracingArgumentsParser;
+import org.tizen.dynamicanalyzer.common.DAResult;
 import org.tizen.dynamicanalyzer.util.Logger;
 
 /**
@@ -24,7 +26,13 @@ public class StartCommand extends Command {
 
        @Override
        public boolean checkArgs(String[] args) {
-               // TODO more precise arguments checking
+               try {
+                       TracingArgumentsParser.parse(args);
+               } catch (ParseException e) {
+                       System.out.println("Wrong command arguments: " + e.getMessage());
+                       return false;
+               }
+
                return true;
        }
 
@@ -42,20 +50,31 @@ public class StartCommand extends Command {
 
        @Override
        public int processCommand(String[] args) {
+               TracingArguments parsedArgs;
+
                try {
-                       // try to get PM proxy and auto start PM if needed
-                       ProcessManagerMBean pmProxy = ClientUtils.getProcessManagerProxy(true);
-                       pmProxy.startTracing();
-               } catch (IOException e) {
-                       Logger.error("Error while communicating with proccess manager: %s.", e.toString());
+                       parsedArgs = TracingArgumentsParser.parse(args);
+               } catch (ParseException e) {
+                       System.out.println("Wrong command arguments: " + e.getMessage());
                        return 1;
+               }
+
+               DAResult result;
+               try {
+                       result = CliInternals.startTracing(parsedArgs);
                } catch (InterruptedException e) {
-                       Logger.warning("Communication with process manager was interrupted.");
-                       // restore interruption flag
-                       Thread.currentThread().interrupt();
+                       System.out.println("Start tracing operation was interrupted.");
+                       return 1;
+               }
+
+               if (!result.isSuccess()) {
+                       System.out.println("Can't start tracing. Error: " + result.getMessage());
                        return 1;
                }
 
+               System.out.println("DA tracing started.");
+               System.out.printf("Run “dacli stop %s” to finish it.%n", parsedArgs.getDevice());
+
                return 0;
        }
 }
index d786a1f..2e81a4f 100644 (file)
 package org.tizen.dynamicanalyzer.cli.manager;
 
-import org.tizen.dynamicanalyzer.cli.tracing.TracingProcess;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.tizen.dynamicanalyzer.cli.tracing.TracingArguments;
+import org.tizen.dynamicanalyzer.common.DAResult;
+import org.tizen.dynamicanalyzer.common.DAResult.ErrorCode;
+import org.tizen.dynamicanalyzer.util.Logger;
 
 /**
- * Class is supposed to manage {@link TracingProcess} instances.
+ * Class offers high level functionality to manage tracing activities
+ * on target devices.
  */
 public class ProcessManager implements ProcessManagerMBean {
+       /**
+        * Timeout for stop tracing command.
+        */
+       static int TRACING_PROCESS_STOP_TIMEOUT = 2000; // TODO measure this timeout more accurate later
+
+       /**
+        * Map between device and last trace manager used for that device.
+        */
+       Map<String, TracingProcessManager> mTracingMap;
+
+       /**
+        * Public constructor.
+        */
        public ProcessManager() {
+               mTracingMap = new ConcurrentHashMap<>();
+       }
+
+       /**
+        * Check whether manager is ready to start tracing on device.
+        *
+        * @param device target device
+        * @return <code>true</code> if device is ready, <code>false</code> otherwise
+        */
+       private boolean isDeviceReady(String device) {
+               TracingProcessManager tpManager = mTracingMap.get(device);
+               return (tpManager == null || tpManager.isFinished());
+       }
+
+       @Override
+       public synchronized String[] getTracedDevices() {
+               ArrayList<String> tracedDevices = new ArrayList<>(mTracingMap.size());
+               for (Entry<String, TracingProcessManager> entry : mTracingMap.entrySet()) {
+                       if (!entry.getValue().isFinished())
+                               tracedDevices.add(entry.getKey());
+               }
+
+               Collections.sort(tracedDevices);
+               return tracedDevices.toArray(new String[0]);
        }
 
        @Override
-       public void getTracedDevices() {
+       public synchronized TracingProcessContext getContext(String device) {
+               TracingProcessManager tpManager = mTracingMap.get(device);
+               if (tpManager == null)
+                       return null;
+
+               return tpManager.getContext();
        }
 
        @Override
-       public void startTracing() {
+       public synchronized DAResult startTracing(TracingArguments args) {
+               String device = args.getDevice();
+
+               if (!isDeviceReady(device)) {
+                       Logger.error("Device '%s' is occupied by another tracing process.", device);
+                       return new DAResult(ErrorCode.ERR_MSG_START_FAIL, "Device is occupied by another tracing process.");
+               }
+
+               final TracingProcessManager tpManager;
+               try {
+                       tpManager = TracingProcessManager.createTracingProcess(args);
+               } catch (IOException e) {
+                       Logger.error("Couldn't start tracing proccess: %s.", e.toString());
+                       return new DAResult(ErrorCode.ERR_MSG_START_FAIL, "Couldn't start tracing process: " + e.toString());
+               }
+
+               mTracingMap.put(device, tpManager);
+
+               return DAResult.SUCCESS;
        }
 
        @Override
-       public void stopTracing() {
+       public synchronized DAResult stopTracing(String device)  {
+               TracingProcessManager tpManager = mTracingMap.get(device);
+               if (tpManager == null) {
+                       Logger.warning("Requested stop tracing on unknown device '%s'.", device);
+                       return new DAResult(ErrorCode.ERR_EXCEPTION_OCCURRED, "There are no tracing proccesses on that device.");
+               }
+
+               if (tpManager.isFinished()) {
+                       Logger.warning("Requested stop for tracing process that is not running '%s'.", device);
+                       return new DAResult(ErrorCode.ERR_EXCEPTION_OCCURRED, "Tracing process is not running.");
+               }
+
+               boolean softStopped;
+               try {
+                       softStopped = tpManager.stopTracing(TRACING_PROCESS_STOP_TIMEOUT);
+               } catch (InterruptedException e) {
+                       Logger.warning("Stopping tracing process was interrupted on device '%s'.", device);
+                       return new DAResult(ErrorCode.ERR_EXCEPTION_OCCURRED, "Stopping tracing process was interrupted.");
+               }
+
+               if (!softStopped) {
+                       Logger.error("Tracing process will be forcibly terminated due timeout %d ms on device '%s'.", TRACING_PROCESS_STOP_TIMEOUT, device);
+                       tpManager.forceStopTracing();
+                       return new DAResult(ErrorCode.ERR_EXCEPTION_OCCURRED, "Tracing process was forcibly terminated, traced data can be corrupted.");
+               }
+
+               Logger.info("Tracing process was successufully stopped on device '%s'.", device);
+               return DAResult.SUCCESS;
        }
 
        @Override
-       public void shutdown() {
+       public synchronized DAResult shutdown() {
+               // TODO shutdown all tracing processes
+               return DAResult.SUCCESS;
        }
 
 }
index 1941a14..b195732 100644 (file)
@@ -1,13 +1,57 @@
 package org.tizen.dynamicanalyzer.cli.manager;
 
+import org.tizen.dynamicanalyzer.cli.tracing.TracingArguments;
+import org.tizen.dynamicanalyzer.common.DAResult;
+import org.tizen.dynamicanalyzer.common.DAResult.ErrorCode;
+
+
+
 /**
  * Interface for Tracing Processes Management.
  */
 public interface ProcessManagerMBean {
-       void getTracedDevices();
 
-       void startTracing();
-       void stopTracing();
+       /**
+        * Get list of all devices on which tracing is currently running.
+        * Returned list is lexicographically sorted.
+        *
+        * @return array of device identifiers
+        */
+       String[] getTracedDevices();
+
+       /**
+        * Get information about last tracing process on specified device.
+        *
+        * @param device device identifier
+        * @return data structure for tracing process,
+        *                 <code>null</code> if tracing process doesn't present
+        */
+       TracingProcessContext getContext(String device);
+
+       /**
+        * Start tracing process with specified tracing arguments.
+        *
+        * @param args tracing arguments
+        * @return {@link ErrorCode#SUCCESS} if tracing process started successfully
+        *         {@link ErrorCode#ERR_MSG_START_FAIL} if can't start tracing for some reason
+        */
+       DAResult startTracing(TracingArguments args);
+
+       /**
+        * Stop tracing process on device with specified identifier.
+        * This operation blocks caller thread.
+        *
+        * @param device device identifier
+        * @return {@link ErrorCode#SUCCESS} if tracing process stopped successfully
+        *                 {@link ErrorCode#ERR_EXCEPTION_OCCURRED} in case of any error
+        */
+       DAResult stopTracing(String device);
 
-       void shutdown();
+       /**
+        * Stop all tracing processes and free process manager resources.
+        *
+        * @return {@link ErrorCode#SUCCESS} if tracing processes finished and resources freed
+        *                 {@link ErrorCode#ERR_EXCEPTION_OCCURRED} in case of any error
+        */
+       DAResult shutdown();
 }
diff --git a/org.tizen.dynamicanalyzer.cli/test/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerTest.java b/org.tizen.dynamicanalyzer.cli/test/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerTest.java
new file mode 100644 (file)
index 0000000..a434890
--- /dev/null
@@ -0,0 +1,172 @@
+package org.tizen.dynamicanalyzer.cli.manager;
+
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.tizen.dynamicanalyzer.cli.tracing.TracingArguments;
+import org.tizen.dynamicanalyzer.cli.tracing.TracingProcess;
+import org.tizen.dynamicanalyzer.common.DAResult;
+import org.tizen.dynamicanalyzer.common.DAResult.ErrorCode;
+import org.tizen.dynamicanalyzer.setting.Template;
+import org.tizen.dynamicanalyzer.util.InternalLogger;
+import org.tizen.dynamicanalyzer.util.Logger;
+
+/**
+ * Test ProcessManager behavior.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({TracingProcessManager.class, ProcessManager.class})
+public class ProcessManagerTest {
+
+       private TracingArguments args1, args2;
+
+       private ProcessManager pm;
+
+       @Mock
+       private TracingProcessManager mgr1;
+
+       @Mock
+       private TracingProcessManager mgr2;
+
+       private TracingProcessContext ctx1;
+
+       private TracingProcessContext ctx2;
+
+       @Before
+       public void setUp() {
+               args1 = new TracingArguments().
+                               setDevice("DEV_1").
+                               setApplication("APP_1").
+                               setTemplate(Template.TEMPLATE_BOTTLENECK).
+                               setDuration(0);
+
+               args2 = new TracingArguments().
+                               setDevice("DEV_2").
+                               setApplication("APP_2").
+                               setTemplate(Template.TEMPLATE_BOTTLENECK).
+                               setDuration(0);
+
+               ctx1 = new TracingProcessContext(args1);
+
+               ctx2 = new TracingProcessContext(args2);
+
+               pm = new ProcessManager();
+
+               Logger.init(InternalLogger.DEBUG);
+
+               PowerMockito.mockStatic(TracingProcessManager.class);
+
+       }
+
+       private void setupManagers() throws Exception {
+               PowerMockito.when(TracingProcessManager.createTracingProcess(args1)).thenReturn(mgr1);
+               Mockito.when(mgr1.getContext()).thenReturn(ctx1);
+               Mockito.when(mgr1.isFinished()).thenReturn(false);
+
+               PowerMockito.when(TracingProcessManager.createTracingProcess(args2)).thenReturn(mgr2);
+               Mockito.when(mgr2.getContext()).thenReturn(ctx2);
+               Mockito.when(mgr2.isFinished()).thenReturn(false);
+       }
+
+       /**
+        * Check manager ability to start tracing process.
+        */
+       @Test
+       public void startTracing_success() throws Exception {
+               setupManagers();
+
+               DAResult result = pm.startTracing(args1);
+
+               assertEquals(ErrorCode.SUCCESS.getErrorNumber(), result.getErrorNumber());
+
+               PowerMockito.verifyStatic();
+               TracingProcessManager.createTracingProcess(args1);
+
+               assertEquals(ctx1, pm.getContext(args1.getDevice()));
+       }
+
+       /**
+        * Check manager doesn't start tracing process if tracing is already running.
+        */
+       @Test
+       public void startTracing_duplicate() throws Exception {
+               startTracing_success();
+
+               DAResult result = pm.startTracing(args1);
+
+               PowerMockito.verifyZeroInteractions(TracingProcess.class);
+
+               assertNotEquals(ErrorCode.SUCCESS.getErrorNumber(), result.getErrorNumber());
+       }
+
+       /**
+        * Check manager correctly responds with list of currently traced devices.
+        * Test one device present.
+        */
+       @Test
+       public void getTracedDevices_one_device() throws Exception {
+               setupManagers();
+
+               DAResult result = pm.startTracing(args2);
+
+               assertEquals(ErrorCode.SUCCESS.getErrorNumber(), result.getErrorNumber());
+               assertFalse(pm.getContext(args2.getDevice()).isFinished());
+
+               assertArrayEquals(
+                               new String[]{
+                                       args2.getDevice()
+                               },
+                               pm.getTracedDevices());
+       }
+
+       /**
+        * Check manager correctly responds with list of currently traced devices.
+        * Test two devices present.
+        */
+       @Test
+       public void getTracedDevices_multiple_devices() throws Exception {
+               getTracedDevices_one_device();
+
+               DAResult result = pm.startTracing(args1);
+
+               assertEquals(ErrorCode.SUCCESS.getErrorNumber(), result.getErrorNumber());
+               assertFalse(pm.getContext((args1.getDevice())).isFinished());
+
+               assertArrayEquals(
+                               new String[]{
+                                       args1.getDevice(),
+                                       args2.getDevice()
+                               },
+                               pm.getTracedDevices());
+
+       }
+
+       /**
+        * Check manager correctly responds with list of currently traced devices.
+        * Test  one device disappeared.
+        */
+       @Test
+       public void getTracedDevices_disappeared() throws Exception {
+               getTracedDevices_multiple_devices();
+
+               ctx1.finishContext(100500);
+               Mockito.when(mgr1.isFinished()).thenReturn(true);
+
+               assertArrayEquals(
+                               new String[]{
+                                       args2.getDevice()
+                               },
+                               pm.getTracedDevices());
+       }
+}