From: Vladislav Eliseev Date: Fri, 22 Apr 2016 13:56:33 +0000 (+0300) Subject: SRADA-215: implemented tracing management and start command X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=886559b071470128a56d94cefa33bf2deaca9988;p=sdk%2Ftools%2Fdynamic-analyzer.git SRADA-215: implemented tracing management and start command Now ProcessManager offers high level interface to start and stop tracing processes and refresh and answer their states. Change-Id: I678597b1316fb0fdf82ab57aa845eb3c3ea5e09a --- diff --git a/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/CliInternals.java b/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/CliInternals.java index 6c195b6..caccc3e 100644 --- a/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/CliInternals.java +++ b/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/CliInternals.java @@ -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); + } } diff --git a/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/commands/StartCommand.java b/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/commands/StartCommand.java index 8810f42..f658525 100644 --- a/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/commands/StartCommand.java +++ b/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/commands/StartCommand.java @@ -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; } } diff --git a/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManager.java b/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManager.java index d786a1f..2e81a4f 100644 --- a/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManager.java +++ b/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManager.java @@ -1,28 +1,128 @@ 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 mTracingMap; + + /** + * Public constructor. + */ public ProcessManager() { + mTracingMap = new ConcurrentHashMap<>(); + } + + /** + * Check whether manager is ready to start tracing on device. + * + * @param device target device + * @return true if device is ready, false otherwise + */ + private boolean isDeviceReady(String device) { + TracingProcessManager tpManager = mTracingMap.get(device); + return (tpManager == null || tpManager.isFinished()); + } + + @Override + public synchronized String[] getTracedDevices() { + ArrayList tracedDevices = new ArrayList<>(mTracingMap.size()); + for (Entry 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; } } diff --git a/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerMBean.java b/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerMBean.java index 1941a14..b195732 100644 --- a/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerMBean.java +++ b/org.tizen.dynamicanalyzer.cli/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerMBean.java @@ -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, + * null 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 index 0000000..a434890 --- /dev/null +++ b/org.tizen.dynamicanalyzer.cli/test/src/org/tizen/dynamicanalyzer/cli/manager/ProcessManagerTest.java @@ -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()); + } +}