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 {
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);
+ }
}
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;
/**
@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;
}
@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;
}
}
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;
}
}
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();
}
--- /dev/null
+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());
+ }
+}