diff options
author | moberhuber | 2008-01-10 19:58:38 +0000 |
---|---|---|
committer | moberhuber | 2008-01-10 19:58:38 +0000 |
commit | 6459bb640308817d92790bb1b7b61348c5418ba8 (patch) | |
tree | 90d5150437b289ade70b25914ddc9b3b153332f8 | |
parent | 7a412ee51d3cf40ec6094533fa753b9d01bd0a6a (diff) | |
download | org.eclipse.tcf-6459bb640308817d92790bb1b7b61348c5418ba8.tar.gz org.eclipse.tcf-6459bb640308817d92790bb1b7b61348c5418ba8.tar.xz org.eclipse.tcf-6459bb640308817d92790bb1b7b61348c5418ba8.zip |
tcf-0.1.0 initial contribution
217 files changed, 30700 insertions, 0 deletions
diff --git a/docs/.project b/docs/.project new file mode 100755 index 000000000..536d25a62 --- /dev/null +++ b/docs/.project @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>tcf_docs</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ </buildSpec>
+ <natures>
+ </natures>
+</projectDescription>
diff --git a/docs/TCF Architecture.png b/docs/TCF Architecture.png Binary files differnew file mode 100644 index 000000000..fbf650ca0 --- /dev/null +++ b/docs/TCF Architecture.png diff --git a/docs/TCF Context Identifier Explanation.html b/docs/TCF Context Identifier Explanation.html new file mode 100644 index 000000000..79e5d09f4 --- /dev/null +++ b/docs/TCF Context Identifier Explanation.html @@ -0,0 +1,257 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<HTML> +<HEAD> + <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1251"> + <TITLE>TCF Context Identifier Explanation</TITLE> + <META NAME="GENERATOR" CONTENT="OpenOffice.org 2.2 (Win32)"> + <META NAME="CREATED" CONTENT="20070830;12134342"> + <META NAME="CHANGEDBY" CONTENT="Eugene Tarassov"> + <META NAME="CHANGED" CONTENT="20070830;12351368"> + <STYLE TYPE="text/css"> + <!-- + H1 { color: #000000 } + P { color: #000000 } + P.western { font-size: 13pt } + H2 { color: #000000 } + --> + </STYLE> +</HEAD> +<BODY LANG="en-US" TEXT="#000000" DIR="LTR"> +<P CLASS="western" STYLE="border-top: none; border-bottom: 1.00pt solid #4f81bd; border-left: none; border-right: none; padding-top: 0in; padding-bottom: 0.06in; padding-left: 0in; padding-right: 0in"> +<FONT COLOR="#17365d"><FONT FACE="Cambria"><FONT SIZE=6 STYLE="font-size: 26pt">TCF +Context Identifier Explanation</FONT></FONT></FONT></P> +<P CLASS="western"><FONT COLOR="#4f81bd"><FONT FACE="Cambria"><FONT SIZE=3><I>Felix +Burton, Wind River, Version 2</I></FONT></FONT></FONT></P> +<H1><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>Introduction</B></FONT></FONT></FONT></H1> +<P CLASS="western">Most if not all TCF services functions need some +way to identify what entity e.g. process, thread, task, semaphore, +breakpoint, flash device, device on JTAG scan chain, etc they should +operate on. To do this TCF uses a context identifier (aka ContextId). +This document is attempting to explain how ContextIds are intended to +be used. This is document does not define actual services or exact +context hierarchies, but for the purpose of making things more +concrete examples may be used.</P> +<H2 LANG="en-GB" STYLE="margin-top: 0in; margin-bottom: 0.04in"><FONT COLOR="#4f81bd"><FONT FACE="Cambria"><FONT SIZE=3 STYLE="font-size: 13pt"><B>Why +a single ContextId?</B></FONT></FONT></FONT></H2> +<P CLASS="western">A prudent question to ask is why use a single +ContextId instead of having separate IDs for each notion e.g. a +ProcessId, ThreadId, BreakpointId, JTAGDeviceId, etc. Having separate +IDs is used in many existing debug APIs and protocols and may seem +intuitive. However, there are several issues with this approach:</P> +<P CLASS="western">1. It is inflexible in that it requires each +function to upfront know how many levels are needed and what type of +context each level represent.</P> +<P CLASS="western">2. This in turn makes it difficult to use the same +API for different environments since they often have different types +of IDs and has different number of levels. For example Linux have +processes and threads while OCD have cores.</P> +<H1 LANG="en-GB"><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>Context +identifier</B></FONT></FONT></FONT></H1> +<P CLASS="western">ContextIds are opaque handles that only have +meaning to the service that created them or its peer services. They +are created for clients, by service implementations to identify some +entity handled by the services. Clients can use contextIds in the +following ways:</P> +<P CLASS="western">1. Pass to the originating service or peer +services</P> +<P CLASS="western">2. Compare for equality with other contextIds +retrieved from the originating service or peer services.</P> +<P CLASS="western">More specifically, clients should not try to +decode or extract information from the contextId, instead they should +make requests to the originating service or peer services using the +contextId for information or action.</P> +<P CLASS="western">As can be seen from the above, contextIds created +by one service can be used by its peer services. The service should +either to do something useful or to give an error indicating that the +contextId is not relevant to that particular service. To guarantee +that a contextId created by service A and passed to service B is not +misinterpreted to be something other that what service A intended, +there must be a global naming scheme for contextId within a target.</P> +<P CLASS="western">This allows two or more services to create the +same contextId when they operate on the same entity. It means that a +single contextId can have multiple aspects that are handled by +different services, thereby allowing decoupling of service +interfaces.</P> +<H1 LANG="en-GB"><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>Context +hierarchies</B></FONT></FONT></FONT></H1> +<P CLASS="western">Entities represented by contextIds typically +relate to similar entities in a list or parent/child relationship. +Examples, 1) Linux processes have children threads, 2) a suspended +thread has a list of stack frames, and 3) threads have register +groups which have registers which can have fields. These +relationships form context hierarchies.</P> +<P CLASS="western">Depending on the system there may be several +different context hierarchies. For example contexts available for +JTAG debugging include:</P> +<P CLASS="western">1. debugging</P> +<P CLASS="western">2. memory access</P> +<P CLASS="western">3. register access</P> +<P CLASS="western">4. JTAG access</P> +<P CLASS="western">Interestingly there may also be relations between +the different hierarchies. For example contexts available for +debugging may correspond with contexts available for memory access. A +typical example of this is Linux where a contextId representing a +process can be used for debugging as well as memory access, open file +table access, memory map access, etc. In such cases, the same +contextId should be used in all hierarchies. This allows clients to +detect when hierarchies come together or split apart so the client +can represent the relationships properly to the user for example in a +GUI.</P> +<H1 LANG="en-GB"><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>Accessing +context information</B></FONT></FONT></FONT></H1> +<P CLASS="western">Information associated with a contextId can be +sufficiently large to make it impractical to transfer all associated +information to the client in a single request. To reduce the amount +of information transferred while still allowing the implementation to +be relatively simple; the information is categorized as follows:</P> +<P CLASS="western">1. Child context references per service</P> +<P CLASS="western">2. Slow changing properties per service, a.k.a. +properties</P> +<P CLASS="western">3. Fast changing properties per service, a.k.a. +state or status +</P> +<P CLASS="western">Category 1 provides a simple way to express +unbounded lists of related contextIds. If such a list becomes too +large the service can split the list into a list of lists, list of +lists or lists, etc as needed.</P> +<P CLASS="western">Category 2 and 3 provides a simple way to express +arbitrary information about the context in the form of a key/value +pair. Properties may also contain contextId references for example +for the parent context.</P> +<P CLASS="western">The split between category 2 and 3 allows the +service to handle fast changing information in a more optimal way and +allows it to handle slow changing information in a more simple way. +It is up to the service to define what information is slow vs. fast +changing.</P> +<H1 LANG="en-GB"><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>ContextId +formatting</B></FONT></FONT></FONT></H1> +<P CLASS="western">The ContextId is represented as string between +clients and services. The formatting of the string with one exception +is completely up to the implementation that created the contextId. +The exception is the ContextId prefix explained below. The remainder +of the string can be formatted in any way that the service descries. +Two typical ways comes to mind:</P> +<P CLASS="western">1. Hierarchical list where each level is spelled +out. For example on Linux:</P> +<P CLASS="western" STYLE="margin-left: 0.79in">a. A process could be +identified by “ppid” and a thread by “ppid,ttid”</P> +<P CLASS="western" STYLE="margin-left: 0.79in">b. A register set by +“ppid,ttid,rset”</P> +<P CLASS="western" STYLE="margin-left: 0.79in">c. A stack frame by +“ppid,ttid,slevel”</P> +<P CLASS="western" STYLE="margin-left: 0.79in">d. A local variable on +a specific stack level by “ppid,ttid,slevel,vname”</P> +<P CLASS="western">2. Flat ID that the generating service used to do +table lookup for more information. For example</P> +<P CLASS="western" STYLE="margin-left: 0.79in">a. Index into an array +“tableIndex,generationNumber”</P> +<P CLASS="western" STYLE="margin-left: 0.79in">b. Key used for hash +lookup “sequentialNumber”</P> +<H1 LANG="en-GB"><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>ContextId +prefix</B></FONT></FONT></FONT></H1> +<P CLASS="western">When information from more than one channel is +joined together to when value-adding services between the two +endpoints create contextIds it must be possible to for every service +to determine if a contextId was created by it or a foreign entity. To +do this, each service manager is assigned a unique contextId prefix +that all its generated contextIds should be prefixed with followed by +the colon (:) character. For example imagine that GDB was designed to +be a value-adding service, contextIds created on this level could be +prefixed by “gdb:” to guarantee that the target would be +able to return error if such contextId was given to it instead of to +the services in GDB.</P> +<P CLASS="western">The prefix used by a service manager is +dynamically assigned by the client initiating the connection. A +limited TCF endpoint implementation is not required to support +contextId prefixing. However, in such case it is only be possible to +have value-adding services if they intercept all services on the +endpoint.</P> +<H1 LANG="en-GB"><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>Context +information caching</B></FONT></FONT></FONT></H1> +<P CLASS="western">Clients will most likely need to cache context +information in order to keep the amount of information transferred to +a minimum. Such caching should be based on the contextId, service +name, and type of data i.e. children contextIds, properties or state.</P> +<P CLASS="western">The suggested implementation is to use a two stage +cache lookup, where the first stage is using only the contextId and +the second stage using the service name and the type of data. The +reason for the two stage approach is to allow easy flushing of the +cached information when contextIds are removed.</P> +<P CLASS="western">Services support caching in clients by sending +events for any changes to the information. The following events are +expected to be generated by services when needed:</P> +<P CLASS="western">1. Children added. The event includes the parent +contextId, service name and list of contextIds and their properties +to be added to the cache. Clients that have not populated the cache +for the specified parent contextId should ignore this event.</P> +<P CLASS="western">2. Children removed. The event includes the parent +contextId, service name and list of contextIds to be removed from the +list. When received, clients should update cache by removing all +listed contextIds for the specified parent contextId and service +name.</P> +<P CLASS="western">3. Children changed. The event includes the parent +contextId and service name. This event does not include the updated +list of contextIds; instead clients are expected to reread the list +of children if they need it. When received, clients should invalidate +the list of children contextIds for the specified parent contextId +and service name.</P> +<P CLASS="western">4. Properties changed. This event includes a list +of contextId, service name and properties. When received, clients +should update cache with the new properties.</P> +<P CLASS="western">5. State or status changed. This event includes +contextId, service name and state or status. When received, clients +should update cache with the new state or status.</P> +<P CLASS="western">Invalidating or removing entries from the list of +children contextIds should also result in recursively invalidating +all cache entries for the removed contextIds. This is necessary to +avoid stale cache entries to linger when a removed contextId is +reused for a new context.</P> +<H1 LANG="en-GB"><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>Relationship +between services</B></FONT></FONT></FONT></H1> +<P CLASS="western">Even though service interfaces should not have any +direct dependencies, they can have context hierarchy relationships.</P> +<P CLASS="western">A good example of such relationship is between the +“run control” service and the “memory” +service. It seems to make sense to specify that the run control +hierarchy is “rooted” in the memory hierarchy since it is +hard to imagine executing instructions without a memory that stores +the instructions.</P> +<P CLASS="western">Another example is for run control, register and +stack trace services where it seems logical to define registers and +stack frame hierarchies to be “rooted” in the run control +hierarchy.</P> +<P CLASS="western">By “rooted” we mean that roots for one +hierarchy can be found in another hierarchy.</P> +<P CLASS="western">Usually clients need only one particular hierarchy +at the time, however some clients, for example in Eclipse the Debug +View is designed to be provide selection for run control, memory +view, locals view, registers view, etc in one place, so it needs to +merge memory, run control and stack trace hierarchies in order to +provide single tree for selection.</P> +<P CLASS="western">The services interface specification should define +the rooting of its context hierarchy, if any. As mentioned in the +example above, run control service is rooted in the memory hierarchy, +and register and stack trace services are rooted in the run control +hierarchy.</P> +<P CLASS="western">It may be possible to a service context hierarchy +to be rooted in multiple hierarchies.</P> +<P CLASS="western">Which context hierarchies are merged is up to the +implementer of the client.</P> +<H1 LANG="en-GB"><FONT COLOR="#365f91"><FONT FACE="Cambria"><FONT SIZE=4><B>Context +hierarchy roots</B></FONT></FONT></FONT></H1> +<P CLASS="western">For some services it is possible to use “null” +as a special parent contextId to the “get children” +command to retrieve a list of root contextIds. The service interface +definition should specify if retrieval of roots is supported by the +service.</P> +<P CLASS="western">Example services that would support the “null” +parent contextId are JTAG access and kernel awareness services since +this is global information in the target.</P> +<P CLASS="western">Example services that would not support the “null” +parent contextId are register and stack trace services since parent +contextId for registers and stack frames is usual obtained through +run control service.</P> +<P CLASS="western"><BR><BR> +</P> +</BODY> +</HTML>
\ No newline at end of file diff --git a/docs/TCF Getting Started.html b/docs/TCF Getting Started.html new file mode 100755 index 000000000..05d2ed32a --- /dev/null +++ b/docs/TCF Getting Started.html @@ -0,0 +1,226 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework: Getting Started</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework: Getting Started</h1> + +<p>Copyright (c) 2007 Wind River Systems, Inc. Made available under the EPL v1.0 +<p>Direct comments, questions to the <a href="mailto:dsdp-tm-dev@eclipse.org">dsdp-tm-dev@eclipse.org</a> mailing list + +<h2>Table of Contents</h2> +<ul> + <li><a href='#Workspace'>Creating Eclipse Workspace</a> + <li><a href='#Plugins'>TCF Plugins</a> + <li><a href='#Agent'>Building TCF Agent</a> + <li><a href='#Browsing'>Browsing Agent Source Code in CDT</a> + <li><a href='#RSE'>Using TCF With Remote System Explorer</a> + <li><a href='#Debugger'>Using TCF With Eclipse Debugger</a> +</ul> + +<h2><a name='Workspace'>Creating Eclipse Workspace</a></h2> + +<p>Eclipse can be used for developing clients for TCF in Java. +TCF host side code is organized into several Eclipse plug-in projects, +below are steps to create and populate Eclipse workspace with TCF projects:</p> + +<ul> + <li>Install JDK 1.5.0 or later + <li>Install <b>Eclipse Classic SDK 3.3</b> or later, last tested with 3.4M3, recommended 3.3.1.1<br> + <a href='http://download.eclipse.org/eclipse/downloads/'>http://download.eclipse.org/eclipse/downloads/</a> + <li>Install <b>TM RSE SDK 2.0</b> or later, last tested with 3.0M3, recommended 2.0.2<br> + <a href='http://download.eclipse.org/dsdp/tm/downloads/'>http://download.eclipse.org/dsdp/tm/downloads/</a> + <li><b>Optional</b> dependencies for TCF/DSF/CDT integration: these are not required by + TCF itself, and not needed for RSE integration or for TCF based debugger demo, + use these for future development: + <ul> + <li>CDT 4.0 or later, last tested with 5.0M3, recommended 4.0.1<br> + <a href='http://download.eclipse.org/tools/cdt/releases/europa/'>http://download.eclipse.org/tools/cdt/releases/europa/</a> + <li>DD DSF SDK N20071113-0200 (Must use this version, later versions will NOT work)<br> + <a href='http://download.eclipse.org/dsdp/dd/downloads/'>http://download.eclipse.org/dsdp/dd/downloads/</a> + </ul></li> + <li>Unzip TCF code. + <li>Run Eclipse: + <pre>eclipse.exe -vm <JDK path>\bin\javaw.exe -data <Workspace path> -vmargs -Xmx200M</pre> + <li>Open "Java" perspective. + <li>In "Package Explorer" view: do right click and then select "Import...". + <li>Select "General/Existing Projects into Workspace" and click "Next". + <li>Select root directory: <TCF Root>\plugins, and click "Next".<ul> + <li>If DD-DSF and/or CDT are not installed, don't import the following two plugins + into your workspace:<ul> + <li>com.windriver.tcf.dsf.core + <li>com.windriver.tcf.dsf.ui + </ul></li> + </ul></li> + <!-- + <li><b>Optional</b> for browsing dependencies:<ul> + <li>In Package Explorer: do right click and select "Import...". + <li>Select "Plug-in Development/Plug-ins and Fragments" and click "Next". + <li>Select "Import plug-ins and fragments required by existig workspace plug-ins" and click "Next". + <li>Click "Select All", then click "Finish". + </ul></li> + --> +</ul> + +<!-- +<p>Alternative way to get CDT, DSF and RSE installed: +<ul> + <li>Get Eclipse for C/C++ Package from <a href='http://www.eclipse.org/downloads/'> + http://www.eclipse.org/downloads/</a> - it includes CDT. + <li>Do "Help > Software Updates > Find and Install > Search for New Features to Install > Next" + <li>Select "Europa Discovery Site", press "Finish" + <li>Select following: + <ul> + <li>Remote Access and Device Development + <ul> + <li>Remote System Explorer End-User Runtime + <li>Debugger Services Framework end-user and extender SDK + </ul> + </ul> + <li>Press "Select Required" if in Error + <li>Press "Next", "Accept", "Next", "Finish" +</ul> +--> + +<h2><a name='Plugins'>TCF Plugins</a></h2> + +<p>TCF plugins source code is stored in <code><TCF Root>\plugins</code> directory. + +<dl> + <dt><b>com.windriver.tcf.api</b> + <dd>This is the main TCF plugin. It contains the framework itself and interfaces for standard services. + It is the only TCF plugin, which should be required by a TCF client. The rest of TCF plugins are + clients developed as a reference implementation or for demonstration purposes. + <p> + <dt><b>com.windriver.debug.tcf.core,com.windriver.debug.tcf.ui</b> + <dd>This is a prototype code that connects Eclipse Debug Framework and Target Communication Framework. + It allows to launch Eclipse debug session by connecting to a target running TCF agent, + and then perform basic debugging tasks, like resuming, suspending, single-stepping, setting/removing breakpoints, etc. + The code can be used as a reference for developing new TCF clients. + <p> + <dt><b>com.windriver.tcf.dsf.core,com.windriver.tcf.dsf.ui</b> + <dd>This code allows Debugger Services Framework (DSF) clients to access targets using TCF as comminucation protocol. + It includes implementation of DSF services as TCF clients. + <p> + <dt><b>com.windriver.tcf.rse.ui</b> + <dd>This plugin allows Remote System Explorer (RSE) to connect to remote machines using TCF as communication protocol. + It includes implementation of RSE services as TCF clients. +</dl> + +<h2><a name='Agent'>Building TCF Agent</a></h2> + +<p><b>Linux</b> is the recommended evaluation platform, since most TCF services +are implemented. To build the agent: +<ul> + <li>Make sure <code>elfutils-libelf-devel</code> is installed. + On Fedora, it can be installed by command: + <pre>yum install elfutils-libelf-devel</pre> + It also can be built and installed from source code, e.g. + <a href="http://www.mr511.de/software/libelf-0.8.9.tar.gz">http://www.mr511.de/software/libelf-0.8.9.tar.gz</a>: + <pre> + ./configure + make + make install + setenv LD_LIBRARY_PATH /usr/local/lib:$LD_LIBRARY_PATH</pre> + <li>Run <code>make</code> command in <code><TCF Root>/agent</code> directory. + <li>Start agent with <code>./agent</code> command. +</ul> + +<p>On <b>Windows</b>, only the TCF file service is currently implemented in the agent, +so the RSE Processes and Eclipse Debug demos will not work. +For building the agent, there are two possibilities:<ul> +<li>Building with gcc (freely available from <a href="http://wascana.sourceforge.net/">Wascana</a>, +<a href="http://www.cygwin.com">Cygwin</a> or the +<a href="http://www.mingw.org/">MinGW32 project</a>): run +<pre>make -fMakefile_cygwin.mak</pre> +in the agent directory (the Makefile works with mingw, too).</li> + +<li>Building with Microsoft Visual C++: Open workspace file <code><TCF Root>/agent/agent.dsw</code> +and then build and run the agent using Development Studio commands. If getting an error about +<tt>IPHlpApi.h</tt> missing, you'll need to install the latest +<a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=0BAF2B35-C656-4969-ACE8-E4C0C0716ADB&displaylang=en">MS Platform SDK</a>. +For the free <a href="http://www.microsoft.com/express/vc/">Visual C++ Express Edition</a>, the +following changes in settings may be necessary:<ul> + <li>Project > Properties > C/C++ > Preprocessor > Preprocessor Definitions: + add <tt>_CRT_SECURE_NO_DEPRECATE</tt></li> + <li>Project > Properties > Linker > Input > Additional Dependencies : + add <tt>shell32.lib</tt></li> +</ul></li> +</ul></p> + +<p>On <b>VxWorks</b>, the file service as well as most debug services are currently +working. Line number mapping and the SysMonitor service (required for RSE Processes +Demo) are not yet implemented.<br/> +To build the agent: Use Wind River Workbench to create a Kernel Module project out of source code in +<code><TCF Root>/agent</code> directory. Use Workbench commands to build and run the agent.</p> + +<h2><a name='Browsing'>Browsing Agent Source Code in CDT</a></h2> +On Linux, the default configuration from the CDT .project file included in TCF +should be fine for correctly browsing the agent source code. Linux is recommended +for working on the agent anyways, because most features are implemented already. +<p> +On Windows, open Project Properties of the agent project, and under C/C++ General > +Indexer switch the configuration to "Win32 - Cygwin" or "Win32 - DevStudio" +as needed. +<p> +For VxWorks, browsing should be configured automatically through the WR Workbench +Kernel Module Project. + +<h2><a name='RSE'>Using TCF With Remote System Explorer</a></h2> + +<p>Remote System Explorer is an Eclipse based component that allows users to create connections to remote machines and +explore their file systems, see list of processes and access some other resources, like remote shells. +Remote System Explorer has been designed as a flexible, extensible framework to which Eclipse plug-in developers can +contribute their own system definitions, actions, etc.</p> + +<p>Plugin <b>com.windriver.tcf.rse.ui</b> enables use of Processes and Files subsystems of Remote System Explorer over TCF. +It also extends Processes subsystem to include CPU utilization data and some other process attributes in RSE views.</p> + +<p>To connect a remote machine over TCF:</p> +<ul> + <li>Make sure TCF agent is running on remote machine. + <li>Run Eclipse with RSE and TCF plugins installed. + <li>In Eclipse, do "Window/Open Perspective/Remote System Explorer" command. + <li>In "Remote Systems" view: do right click and select "New/Connection..." + <li>In "New Connection" dialog box: select TCF and press "Next" button. + <li>Enter "Host name" - IP host name ot the target machine, and "Connection name" - arbitrary string to name new connection. + Press "Finish" button. + <li>New connection should appear in "Remote Systems" view and it is ready to be explored. +</ul> + +<p>RSE features supported by TCF connection: +<ul> + <li>File Subsystem: full support, i.e. browse, upload, download, copy, move, delete + <li>Processes: browse, including parent/child relationship +</ul> + +<h2><a name='Debugger'>Using TCF With Eclipse Debugger</a></h2> + +<p>Plugins <b>com.windriver.debug.tcf.core</b> and <b>com.windriver.debug.tcf.ui</b> allow to start a debug session +by connecting to a machine runnning TCF agent. This is not a complete debugger implementation, it is intended for +demo and reference purposes. + +<p>To start a debug session over TCF:</p> +<ul> + <li>Make sure TCF agent is running on remote machine. + <li>Run Eclipse with TCF plugins installed. + <li>In Eclipse, do "Window/Open Perspective/Debug" command. + <li>Do "Run/Open Debug Dialog..." command. + <li>In "Debug" dialog box: select "Target Comminucation Framework" configuration type and press "New" button. + <li>Enter a name for the configuration. + <li>Select a target machine in "Available targtes" list. The list shows targets autodetected on local network. + <li>Press "Run Diagnostics" button to test connectivity for selected target. + <li>Enter a program name to run in debug session, for example "/bin/ls". + <li>Press "Debug" to start the debugger. +</ul> + +<p>In TCF debug session only "Debug", "Breakpoints" and "Registers" views are populated. Source level debugging +in not supported at this time. Breakpoints can be planted at an absolute addresses only, using menu command "Run/Toggle Breakpoint". +"Registers" view can be brought in by "Window > Show View > Registers". Registers can be shown only for top stack frame. +</p> + +</body> +</html>
\ No newline at end of file diff --git a/docs/TCF Linux Agent Prototype.html b/docs/TCF Linux Agent Prototype.html new file mode 100644 index 000000000..50ec8e8fe --- /dev/null +++ b/docs/TCF Linux Agent Prototype.html @@ -0,0 +1,199 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<HTML> +<HEAD> + <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1251"> + <TITLE>This is a brief description of the TCF Linux user-mode agent prototype implementation</TITLE> + <META NAME="GENERATOR" CONTENT="OpenOffice.org 2.2 (Win32)"> + <META NAME="CREATED" CONTENT="20070830;12381303"> + <META NAME="CHANGED" CONTENT="16010101;0"> + <STYLE TYPE="text/css"> + <!-- + H1 { color: #000000 } + P { color: #000000 } + P.western { font-size: 13pt } + H2 { color: #000000 } + --> + </STYLE> +</HEAD> +<BODY LANG="en-US" TEXT="#000000" DIR="LTR"> + +<P>This is a brief description of the TCF Linux +user-mode agent prototype implementation. The agent is implemented as +an event driven program. The main event queue is handled by a single +thread – the event dispatch thread. Some sub-systems are using +other threads locally, but will never call other sub-systems using +these threads. Instead an event will be placed on the main event +queue to handle the inter sub-system communication.</P> + +<H1>Main Program</H1> + +<P>Main program parses command line options and initialized sub-systems</P> +<P>Files:</P> +<P>main.c</P> + +<H1>Target Communication Framework</H1> + +<H2>Command and Event Registration and Dispatch</H2> + +<P>This module handles registration of command and +event handlers. It is called when new messages are received and will +dispatch messages to the appropriate handler. It has no knowledge of +what transport protocol is used and what services do.</P> +<P>Files:</P> +<P>protocol.c</P> +<P>protocol.h</P> + +<H2>Transport Layer</H2> + +<P>Implements input and output stream over TCP/IP +transport and UDP based server side auto discovery.</P> +<P>Files:</P> +<P>channel.c</P> +<P>channel.h</P> +<P>tcf.h</P> + +<H2>Input and Output Stream Interface and Library</H2> + +<P>This module defines the input and output stream +interface and support library functions.</P> +<P>Files:</P> +<P>streams.c</P> +<P>streams.h</P> + +<H1>Services</H1> + +<H2>Breakpoint</H2> + +<P>The breakpoint services implements a global +breakpoint list.</P> +<P>Files:</P> +<P>breakpoints.c</P> +<P>breakpoints.h</P> + +<H2>Run Control</H2> + +<P>This module implements the run control service. It +builds uses the context module to do low level control of contexts. +It implements a “safe queue” which contains events that +that should be processed then their context is suspended. Incoming +TCF messages are suspended while the safe queue is non-empty and are +resumed when the last safe queue entry is handled.</P> +<P>Files:</P> +<P>runctrl.c</P> +<P>runctrl.h</P> + +<H2>System Monitoring</H2> + +<P>This module provides system level monitoring +information, similar to the UNIX top or Windows task manager.</P> +<P>Files:</P> +<P>sysmon.c</P> +<P>sysmon.h</P> + +<H2>Agent Diagnostics</H2> + +<P>This service is used to do end-to-end self test +from the host to the target.</P> +<P>Files:</P> +<P>diagnostics.c</P> +<P>diagnostics.h</P> + +<H1>OS Context Handling</H1> + +<P>This module handles process/thread OS contexts and +their state machine. All ptrace() handling is isolated to here.</P> +<P>Files:</P> +<P>context.c</P> +<P>context.h</P> + +<H1>Agent Event Queue</H1> + +<P>This module implements the main event queue +dispatch and queuing. All events are processed by a single thread. +Any thread can queue new events.</P> +<P>Files:</P> +<P>events.c</P> +<P>events.h</P> + +<H1>Misc</H1> + +<H2>Command line interpreter</H2> + +<P>The module allows a user to interact with agent. Current implementation of command line interpreter is incomplete.</P> +<P>Files:</P> +<P>cmdline.c</P> +<P>cmdline.h</P> + +<H2>Error message display</H2> + +<P>This module defines agent error codes in addition to system codes defined in errno.h</P> +<P>Files:</P> +<P>errors.c</P> +<P>errors.h</P> + +<H2>Exception Handling</H2> + +<P>Exception handling. Functionality is similar to C++ try/catch.</P> +<P>Files:</P> +<P>exceptions.c</P> +<P>exceptions.h</P> + +<H2>JSON Library</H2> + +<P>The module contains utility functions for parsing and generating of JSON text. +TCF standard services use JSON as messages format. See <a href='TCF Specification.html#JSON'>JSON - Preferred Marshaling</a> +for JSON description.</P> +<P>Files:</P> +<P>json.c</P> +<P>json.h</P> + +<H2>Double Linked List</H2> + +<P>Utilitity module to support double linked lists.</P> +<P>Files:</P> +<P>link.h</P> + +<H2>Host OS Abstraction</H2> + +<P>Machine and OS dependend definitions. +This module implements host OS abstraction layer that helps make +agent code portable between Linux, Windows, VxWorks and potentially other OSes.</P> +<P>Files:</P> +<P>mdep.c</P> +<P>mdep.h</P> + +<H2>Malloc Abstraction</H2> + +<P>Provides local versions of malloc(), realloc() and free().</P> +<P>Files:</P> +<P>myalloc.c</P> +<P>myalloc.h</P> + +<H2>Proxy</H2> + +<P>Proxy service should allow tunneling of TCF messages to another target on behalf of a client. +This service intended to be used when a client has no direct access to a target.</P> +<P>Files:</P> +<P>proxy.c</P> +<P>proxy.h</P> + +<H2>Test Application</H2> + +<P>Test application is used by Diagnostics service to run various tests.</P> +<P>Files:</P> +<P>test.c</P> +<P>test.h</P> + +<H2>Debug Logging</H2> + +<P>The module implements logging and tracing that is mostly intended for debugging of the agent.</P> +<P>Files:</P> +<P>trace.c</P> +<P>trace.h</P> + +<H1>Architecture</H1> + +<P><IMG SRC="TCF%20Architecture.png" NAME="graphics1" ALIGN=BOTTOM WIDTH=647 HEIGHT=359 BORDER=0></P> + +</BODY> +</HTML> diff --git a/docs/TCF Project.html b/docs/TCF Project.html new file mode 100644 index 000000000..0071e96fc --- /dev/null +++ b/docs/TCF Project.html @@ -0,0 +1,141 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd"> +<HTML lang=en-us> +<HEAD> +<TITLE>Target Communication Framework</TITLE> +</HEAD> + +<BODY> + +<H1>Target Communication Framework project</H1> + +<P> +<H2><A name=Index></A>Index</H2> + +<UL> + <LI><A href="#Summary">Summary</A> + <LI><A href="#Goals">Goals</A> + <LI><A href="#Features">Features</A> + <LI><A href="#Agent">Reference implementation of a target agent</A> + <LI><A href="#Debugger">Prototype of a debugger based on Eclipse Debug Framework and TCF</A> + <LI><A href="#RSE">Prototype of a system monitor and remote file access based on Remote System Explorer and TCF</A> +</UL> + +<P> +<H2><A name=Summary></A>Summary </H2> + +<P>Today almost every device software development tool on the market has its own +method of communication with target system. Communication methods often conflict +with each other, require individual setup, configuration and maintenance, impose +all kinds of unnecessary limitations. Target Communication Framework is designed +to establish common ground in the area of communication protocols between +development tools and embedded devices. + +<P> +<H2><A name=Goals></A>Goals </H2> +<P> +<UL> + <LI>Universal, extensible, simple, lightweight, vendor agnostic framework for + tools and targets to communicate for purpose of debugging, profiling, code + patching and other device software development needs. + <LI>Single configuration per target (not per tool per target as today in most + cases), or no configuration when possible. + <LI>Small overhead and footprint on target side. </LI></UL> +<P> + +<H2><A name=Features></A>Features </H2> +<P><A href="TCF Specification.html">Target Communication Framework Specification</A> is a document +describing design goals, requirements and format of TCF communication protocol, +as well as framework API and software design considerations. +<P>TCF communication model is based on the idea of services. A service is a group +of related commands, events and semantics. A service can be discovered, +added or removed as a group at communication endpoint. Service definitions are +not part of the framework specification, and new services are expected to be +defined by developers of tools and target agents. However, standardization of +common services is needed to achieve certain level of compatibility of +tools/targets, see <A href="TCF Services.html">TCF Services Specification</A> +as starting point of this work. + +<H2><A name=Agent></A>Reference implementation of a target agent</H2> + +<P>Current reference implementation of TCF target agents is fully functional, +can run on Windows, Linux and VxWorks, and provides the following services: +<UL> + <LI>Run Control - provides threads and processes run control functionality + sufficient for debugging of user space programs. On Linux it is implemented + using PTRACE, on VxWorks is uses vxdbgLib, on Windows it is not currently supported. + + <LI>Breakpoints - provides basic breakpoints support. On Linux it is + implemented using PTRACE, on VxWorks is uses vxdbgLib, + on Windows it is not currently supported. + + <LI>Registers - allows inspection and modification of CPU registers. + Implemented for Linux and VxWorks, on Windows it is not currently supported. + + <LI>Stack Trace - execution thread stack back-tracing. + Implemented for Linux and VxWorks, on Windows it is not currently supported. + + <LI>Memory - program memory access. + Implemented for Linux and VxWorks, on Windows it is not currently supported. + + <LI>Processes - provides access to the target OS's process + information, allows starting new and terminating existing processes, + and allows attaching and detaching processes for debugging. + Implemented for Linux and VxWorks, on Windows it is not currently supported. + + <LI>Line Numbers - provides mapping between locations in the source files + and corresponding machine instruction addresses in the executable object. + Implemented for Linux only. + + <LI>Sys Monitor - provides list of processes, process attributes and + CPU/memory utilization data. On Linux it is implemented using /proc file + system, on Windows and VxWorks it is not currently supported. + + <LI>File System - provides access to remote file system. + + <LI>Diagnostics - allows testing of communication channel and agent + functionality. +</UL> + +<P>The agent code is designed to be easily extensible by adding new command +handler implementations. The code separates machine dependences, common TCF +logic and service implementations, which allows easy porting to a new OS or a +target and reconfiguring of the agent for specific needs. The code is written in +ANSI C. See <A href="TCF Linux Agent Prototype.html">TCF Linux Agent Prototype</A> +for more details about the agent code. + +<H2><A name=Debugger></A>Prototype of a debugger based on Eclipse Debug Framework and TCF</H2> + +<P>The prototype code connects Eclipse Debug Framework and Target Communication +Framework. It allows to launch Eclipse debug session by connecting to a target +running TCF agent, and then perform basic debugging tasks, like resuming, +suspending, single-stepping, setting/removing breakpoints, etc. Since current +reference implementation of target agent does not support line number +information access, source level debugging is not supported. +<P>The prototype launch configuration autodetects TCF targets on a local network +and allows a user to connect to a target by simply selecting it from a list +without a need for any further configuration or setup. TCF launch configuration +dialog also offers controls to run a built-in diagnostics on a selecting target, +which perform stress testing of communication channel, agent and target itself: +<P><IMG alt="TCF launch configuration dialog" src="TCF_Launch_Dialog.jpg"> + +<P>The prototype makes use of flexible debug model element hierarchy support, +which is available in Eclipse debug framework since Eclipse 3.2. The flexible +hierarchy allows debugger views to be "data driven" or, in other words, dynamically +adapt to a given targets capabilities and structure, without a need to modify +debugger code to support a new target. + +<H2><A name=RSE></A>Prototype of a system monitor and remote file access based on Remote System Explorer and TCF</H2> + +<P>Remote System Explorer is an Eclipse based component that allows users to +create connections to remote machines and explore their file systems, see list +of processes and access some other resources, like remote shells. Remote System +Explorer has been designed as a flexible, extensible framework to which Eclipse +plug-in developers can contribute their own system definitions, actions, etc. +<P>The prototype enables use of Processes and Files subsystems of Remote System +Explorer over TCF. It also extends Processes subsystem to include CPU +utilization data and some other process attributes in RSE views: +<P><IMG alt="Remote System Explorer: Files subsystem over TCF" src="TCF_RSE_Files.jpg"> +<P><IMG alt="Remote System Explorer: Processes subsystem over TCF" src="TCF_RSE_Processes.jpg"> + +</BODY> +</HTML>
\ No newline at end of file diff --git a/docs/TCF Service - Breakpoints.html b/docs/TCF Service - Breakpoints.html new file mode 100644 index 000000000..a49cdf3ab --- /dev/null +++ b/docs/TCF Service - Breakpoints.html @@ -0,0 +1,408 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services - Breakpoints</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services - Breakpoints</h1> + +<ul> + <li><a href='#Cmds'>Commands</a> + <ul> + <li><a href='#CmdSet'>Set</a> + <li><a href='#CmdAdd'>Add</a> + <li><a href='#CmdChange'>Change</a> + <li><a href='#CmdEnable'>Enable</a> + <li><a href='#CmdDisable'>Disable</a> + <li><a href='#CmdRemove'>Remove</a> + <li><a href='#CmdGetIDs'>Get IDs</a> + <li><a href='#CmdGetProperties'>Get Properties</a> + <li><a href='#CmdGetStatus'>Get Status</a> + </ul> + <li><a href='#Events'>Events</a> + <li><a href='#API'>API</a> +</ul> + +<h1>Breakpoints Service</h1> + +<p>Breakpoint is represented by unique identifier and set of properties. +Breakpoint identifier (String id) needs to be unique across all hosts and targets</p> + +<p>Breakpoint properties is extendable collection of named attributes, +which define breakpoint location and behavior. The service defines some common +attribute names, host tools and target agents may support additional attributes.</p> + +<p>For each breakpoint a target agent maintains another extendable collection of named attributes: +breakpoint status. While breakpoint properties are persistent and represent user input, +breakpoint status reflects dynamic target agent reports about breakpoint current state, +like actual addresses where breakpoint is planted or planting errors.</p> + +<p>Breakpoints are associated with communication channel and traget agent must remove them +when the channel is closed. Target agent should maintain separate breakpoint table +for each communication channel. It is allowed to set same breakpoint (same ID) through multiple +channels, target agent should treat it as single breakpoint with maltiple references. Such breakpoint +is removed when all refering channels are closed.</p> + +<p>The service uses standard format for error reports, +see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p> + +<h2><a name='Cmds'>Commands</a></h2> + +<h3><a name='CmdSet'>Set</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • set • <i><array of breakpoints></i> • + +<i><array of breakpoints></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><breakpoints list></i> ] + +<i><breakpoints list></i> + <font face=Wingdings>Ø</font> <i><breakpoint data></i> + <font face=Wingdings>Ø</font> <i><breakpoint data></i> , <i><breakpoint data></i> + +<i><breakpoint data></i> + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p> The command downloads breakpoints data to a target agent. +The command is intended to be used only to initialize target breakpoints table +when communication channel is open. After that, host should +notify target about (incremental) changes in breakpoint data by sending +Add, Change and Remove commands.<p> + +<p>Breakpoint data consists of a list of breakpoint properties. All properties are optional, except ID. +Tools and targets can define additional properties. Predefined properties are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b></code> + - breakpoint ID + <li><code><b><font face="Courier New" size=2 color=#333399>"Enabled" : <i><boolean></i></font></b></code> + - if true breakpoint is enabled + <li><code><b><font face="Courier New" size=2 color=#333399>"Address" : <i><string></i></font></b></code> + - if preset, defines address of the breakpoint. Address is an expression that evaluates to breakpoint memory address. + Alternatively, breakpoint location can be given as File/Line/Column. + <li><code><b><font face="Courier New" size=2 color=#333399>"Condition" : <i><string></i></font></b></code> + - a conditional expression that is evaluted every time execution hits the breakpoint. If condition is evaluated to false, + the breakpoint is skipped and execution is resumed without sending notifications to a host. + <li><code><b><font face="Courier New" size=2 color=#333399>"File" : <i><string></i></font></b></code> + - source code file name of breakpoint location. + <li><code><b><font face="Courier New" size=2 color=#333399>"Line" : <i><int></i></font></b></code> + - source code line number of breakpoint location. + <li><code><b><font face="Courier New" size=2 color=#333399>"Column" : <i><int></i></font></b></code> + - source code column number of breakpoint location. +</ul> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdAdd'>Add</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • add • <i><breakpoint data></i> • +</font></b></pre> + +<p>The command adds breakpoint to target agent breakpoint table. Host should send this command when user creates new breakpoint</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdChange'>Change</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • change • <i><breakpoint data></i> • +</font></b></pre> + +<p>The command updates breakpoint data in target agent breakpoint table. Host should send this command when user modifies a breakpoint</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdEnable'>Enable</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • enable • <i><array of breakpoint IDs></i> • + +<i><array of breakpoint IDs></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><breakpoint IDs list></i> ] + +<i><breakpoint IDs list></i> + <font face=Wingdings>Ø</font> <i><breakpoint ID></i> + <font face=Wingdings>Ø</font> <i><breakpoint ID></i> , <i><breakpoint ID></i> + +<i><breakpoint ID></i> + <font face=Wingdings>Ø</font> <i><string></i> +</font></b></pre> + +<p>The command enables a list of breakpoints - sets brekpoint property "Enabled" to true.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdDisable'>Disable</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • disable • <i><array of breakpoint IDs></i> • +</font></b></pre> + +<p>The command disables a list of breakpoints - sets brekpoint property "Enabled" to false.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdRemove'>Remove</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • remove • <i><array of breakpoint IDs></i> • +</font></b></pre> + +<p>The command removes a list of breakpoints. Host should send this command when user deletes breakpoints.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdGetIDs'>Get IDs</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • getIDs • +</font></b></pre> + +<p>The command uploads IDs of breakpoints known to target agent. Only breakpoints what are set through +this communication channel are reported. Target agent should maintain separate breakpoint table +for each communication channel.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of breakpoint IDs></i> • +</font></b></pre> + +<h3><a name='CmdGetProperties'>Get Properties</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • getProperties • <i><string: breakpoint ID></i> • +</font></b></pre> + +<p>The command uploads properties of given breakpoint from target agent breakpoint table.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><;breakpoint data></i> • +</font></b></pre> + +<h3><a name='CmdGetStatus'>Get Status</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Breakpoints • getStatus • <i><string: breakpoint ID></i> • +</font></b></pre> + +<p>The command uploads status of given breakpoint from target agent.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><;breakpoint status></i> • + +<i><breakpoint status></i> + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p>Breakpoint stat consists of a list of status properties. All properties are optional. +Tools and targets can define additional properties. Predefined properties are:</p> + +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"Planted" : <i><array of addresses></i></font></b></code> + <li><code><b><font face="Courier New" size=2 color=#333399>"Error" : <i><string></i></font></b></code> + <li><code><b><font face="Courier New" size=2 color=#333399>"File" : <i><string></i></font></b></code> + <li><code><b><font face="Courier New" size=2 color=#333399>"Line" : <i><int></i></font></b></code> + <li><code><b><font face="Courier New" size=2 color=#333399>"Column" : <i><int></i></font></b></code> +</ul> + +<h2><a name='Events'>Events</a></h2> + +<pre><b><font face="Courier New" size=2 color=#333399> +E • Breakpoints • status • <i><string: breakpoint ID></i> • <i><breakpoint status></i> • +</font></b></pre> + +<dl> + <dt><b>status</b> + <dd>is sent when breakpoint status changes. +</dl> + +<h2><a name='API'>API</a></h2> + +<pre> +<font color=#3F5FBF>/** + * Breakpoint is represented by unique identifier and set of properties. + * Breakpoint identifier (String id) needs to be unique across all hosts and targets. + * + * Breakpoint properties (Map<String,Object>) is extendable collection of named attributes, + * which define breakpoint location and behavior. This module defines some common + * attribute names (see PROP_*), host tools and target agents may support additional attributes. + * + * For each breakpoint a target agent maintains another extendable collection of named attributes: + * breakpoint status (Map<String,Object>, see STATUS_*). While breakpoint properties are + * persistent and represent user input, breakpoint status reflects dynamic target agent reports + * about breakpoint current state, like actual addresses where breakpoint is planted or planting errors. + */</font> +<font color=#7F0055>public interface</font> IBreakpoints <font color=#7F0055>extends</font> IService { + + <font color=#3F5FBF>/** + * Service name. + */</font> + <font color=#7F0055>static final</font> String NAME = "Breakpoints"; + + <font color=#3F5FBF>/** + * Breakpoint property names. + */</font> + <font color=#7F0055>static final</font> String + PROP_ID = "ID", // String + PROP_ENABLED = "Enabled", // Boolean + PROP_ADDRESS = "Address", // String + PROP_CONDITION = "Condition", // String + PROP_FILE = "File", // String + PROP_LINE = "Line", // Number + PROP_COLUMN = "Column"; // Number + + <font color=#3F5FBF>/** + * Breakpoint status field names. + */</font> + <font color=#7F0055>static final</font> String + STATUS_PLANTED = "Planted", // Array of addresses + STATUS_ERROR = "Error", // String + STATUS_FILE = "File", // String + STATUS_LINE = "Line", // Number + STATUS_COLUMN = "Column"; // Number + + <font color=#3F5FBF>/** + * Call back interface for breakpoint service commands. + */</font> + <font color=#7F0055>interface</font> DoneCommand { + <font color=#7F0055>void</font> doneCommand(IToken token, Exception error); + } + + <font color=#3F5FBF>/** + * Download breakpoints data to target agent. + * The command is intended to be used only to initialize target breakpoints table + * when communication channel is open. After that, host should + * notify target about (incremental) changes in breakpoint data by sending + * add, change and remove commands. + * + * @param properties - array of breakpoints. + * @param done - command result call back object. + */</font> + IToken set(Map<String,Object>[] properties, DoneCommand done); + + <font color=#3F5FBF>/** + * Called when breakpoint is added into breakpoints table. + * @param properties - breakpoint properties. + * @param done - command result call back object. + */</font> + IToken add(Map<String,Object> properties, DoneCommand done); + + <font color=#3F5FBF>/** + * Called when breakpoint properties are changed. + * @param properties - breakpoint properties. + * @param done - command result call back object. + */</font> + IToken change(Map<String,Object> properties, DoneCommand done); + + <font color=#3F5FBF>/** + * Tell target to change (only) PROP_ENABLED breakpoint property 'true'. + * @param ids - array of enabled breakpoint identifiers. + * @param done - command result call back object. + */</font> + IToken enable(String[] ids, DoneCommand done); + + <font color=#3F5FBF>/** + * Tell target to change (only) PROP_ENABLED breakpoint property to 'false'. + * @param ids - array of disabled breakpoint identifiers. + * @param done - command result call back object. + */</font> + IToken disable(String[] ids, DoneCommand done); + + <font color=#3F5FBF>/** + * Tell target to remove breakpoint. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */</font> + IToken remove(String[] ids, DoneCommand done); + + <font color=#3F5FBF>/** + * Upload IDs of breakpoints known to target agent. + * @param done - command result call back object. + */</font> + IToken getIDs(DoneGetIDs done); + + <font color=#7F0055>interface</font> DoneGetIDs { + <font color=#7F0055>void</font> doneGetIDs(IToken token, Exception error, String[] ids); + } + + <font color=#3F5FBF>/** + * Upload properties of given breakpoint from target agent breakpoint table. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */</font> + IToken getProperties(String id, DoneGetProperties done); + + <font color=#7F0055>interface</font> DoneGetProperties { + <font color=#7F0055>void</font> doneGetProperties(IToken token, Exception error, Map<String,Object> properties); + } + + <font color=#3F5FBF>/** + * Upload status of given breakpoint from target agent. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */</font> + IToken getStatus(String id, DoneGetStatus done); + + <font color=#7F0055>interface</font> DoneGetStatus { + <font color=#7F0055>void</font> doneGetStatus(IToken token, Exception error, Map<String,Object> status); + } + + <font color=#3F5FBF>/** + * Breakpoints service events listener. + */</font> + <font color=#7F0055>interface</font> BreakpointsListener { + + <font color=#3F5FBF>/** + * Called when breakpoint status changes. + * @param id - unique breakpoint identifier. + * @param status - breakpoint status. + */</font> + <font color=#7F0055>void</font> breakpointStatusChanged(String id, Map<String,Object> status); + } + + <font color=#7F0055>void</font> addListener(BreakpointsListener listener); + + <font color=#7F0055>void</font> removeListener(BreakpointsListener listener); +} +</pre> + +</body> +</html> + + diff --git a/docs/TCF Service - File System.html b/docs/TCF Service - File System.html new file mode 100644 index 000000000..711b31fe4 --- /dev/null +++ b/docs/TCF Service - File System.html @@ -0,0 +1,1212 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services - File System</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services - File System</h1> + +<ul> + <li><a href='#ReqSync'>Request Synchronization and Reordering</a> + <li><a href='#FileNames'>File Names</a> + <li><a href='#FileModes'>File Open Modes</a> + <li><a href='#FileAttributes'>File Attributes</a> + <li><a href='#ErrorCodes'>Error Codes</a> + <li><a href='#Cmds'>Commands</a> + <ul> + <li><a href='#CmdOpen'>open</a> + <li><a href='#CmdClose'>close</a> + <li><a href='#CmdRead'>read</a> + <li><a href='#CmdWrite'>write</a> + <li><a href='#CmdStat'>stat</a> + <li><a href='#CmdLStat'>lstat</a> + <li><a href='#CmdFStat'>fstat</a> + <li><a href='#CmdSetStat'>setstat</a> + <li><a href='#CmdFSetStat'>fsetstat</a> + <li><a href='#CmdOpenDir'>opendir</a> + <li><a href='#CmdReadDir'>readdir</a> + <li><a href='#CmdMkDir'>mkdir</a> + <li><a href='#CmdRmDir'>rmdir</a> + <li><a href='#CmdRoots'>roots</a> + <li><a href='#CmdRemove'>remove</a> + <li><a href='#CmdRealPath'>realpath</a> + <li><a href='#CmdRename'>rename</a> + <li><a href='#CmdReadLink'>readlink</a> + <li><a href='#CmdSymLink'>symlink</a> + <li><a href='#CmdSymLink'>copy</a> + <li><a href='#CmdSymLink'>user</a> + </ul> + <li><a href='#API'>API</a> +</ul> + +<h1>File System Service</h1> + +<p>File System service provides file transfer (and more generally file +system access) functionality in TCF. The service design is +derived from SSH File Transfer Protocol specifications.</p> + +<h2><a name='ReqSync'>Request Synchronization and Reordering</a></h2> + +<p>The protocol and implementations MUST process requests relating to +the same file in the order in which they are received. In other +words, if an application submits multiple requests to the server, the +results in the responses will be the same as if it had sent the +requests one at a time and waited for the response in each case. For +example, the server may process non-overlapping read/write requests +to the same file in parallel, but overlapping reads and writes cannot +be reordered or parallelized. However, there are no ordering +restrictions on the server for processing requests from two different +file transfer connections. The server may interleave and parallelize +them at will.</p> + +<p>There are no restrictions on the order in which responses to +outstanding requests are delivered to the client, except that the +server must ensure fairness in the sense that processing of no +request will be indefinitely delayed even if the client is sending +other requests so that there are multiple outstanding requests all +the time.</p> + +<p>There is no limit on the number of outstanding (non-acknowledged) +requests that the client may send to the server. In practice this is +limited by the buffering available on the data stream and the queuing +performed by the server. If the server's queues are full, it should +not read any more data from the stream, and flow control will prevent +the client from sending more requests.</p> + +<h2><a name='FileNames'>File Names</a></h2> + +<p>This protocol represents file names as strings. File names are +assumed to use the slash ('/') character as a directory separator.</p> + +<p>File names starting with a slash are "absolute", and are relative to +the root of the file system. Names starting with any other character +are relative to the user's default directory (home directory). Client +can use 'user()' command to retrieve current user home directory.</p> + +<p>Servers SHOULD interpret a path name component ".." as referring to +the parent directory, and "." as referring to the current directory. +If the server implementation limits access to certain parts of the +file system, it must be extra careful in parsing file names when +enforcing such restrictions. There have been numerous reported +security bugs where a ".." in a path name has allowed access outside +the intended area.</p> + +<p>An empty path name is valid, and it refers to the user's default +directory (usually the user's home directory).</p> + +<p>Otherwise, no syntax is defined for file names by this specification. +Clients should not make any other assumptions; however, they can +splice path name components returned by readdir() together +using a slash ('/') as the separator, and that will work as expected.</p> + +<h2><a name='FileModes'>File Open Modes</a></h2> + +<p>File open mode is bitwise OR of mode flags:</p> + +<dl> + <dt><code>O_READ = 0x00000001</code> + <dd>Open the file for reading. + + <dt><code>O_WRITE = 0x00000002</code> + <dd>Open the file for writing. If both this and O_READ are + specified, the file is opened for both reading and writing. + + <dt><code>O_APPEND = 0x00000004</code> + <dd>Force all writes to append data at the end of the file. + + <dt><code>O_CREAT = 0x00000008</code> + <dd>If this flag is specified, then a new file will be created if one + does not already exist (if O_TRUNC is specified, the new file will + be truncated to zero length if it previously exists). + + <dt><code>O_TRUNC = 0x00000010</code> + <dd>Forces an existing file with the same name to be truncated to zero + length when creating a file by specifying O_CREAT. + O_CREAT MUST also be specified if this flag is used. + + <dt><code>O_EXCL = 0x00000020</code> + <dd>Causes the request to fail if the named file already exists. + O_CREAT MUST also be specified if this flag is used. +</dl> + +<h2><a name='FileAttributes'>File Attributes</a></h2> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><file attributes></i> + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p>All attributes are optional. +Tools and targets can define additional attributes. Predefined attributes are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"Size" : <i><int></i></font></b></code> + - file size in bytes + <li><code><b><font face="Courier New" size=2 color=#333399>"UID" : <i><int></i></font></b></code> + - file owner user ID + <li><code><b><font face="Courier New" size=2 color=#333399>"GID" : <i><int></i></font></b></code> + - file owner group ID + <li><code><b><font face="Courier New" size=2 color=#333399>"Permissions" : <i><int></i></font></b></code> + - file access permissions + <li><code><b><font face="Courier New" size=2 color=#333399>"ATime" : <i><int></i></font></b></code> + - file last access time + <li><code><b><font face="Courier New" size=2 color=#333399>"MTime" : <i><int></i></font></b></code> + - file last modification time +</ul> + +<h2><a name='ErrorCodes'>Error codes</a></h2> + +<p>The service uses standard format for error reports, +see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p> + +<p>Currently, the following values are defined for error code (other values may be +defined by future versions of this protocol):</p> + +<dl> + <dt><code>STATUS_OK = 0</code> + <dd>Indicates successful completion of the operation. + + <dt><code>STATUS_EOF = 1</code> + <dd>Indicates end-of-file condition; for 'read' it means that no + more data is available in the file, and for 'readdir' it + indicates that no more files are contained in the directory. + + <dt><code>STATUS_NO_SUCH_FILE = 2</code> + <dd>This code is returned when a reference is made to a file which + should exist but doesn't. + + <dt><code>STATUS_PERMISSION_DENIED = 3</code> + <dd>is returned when the authenticated user does not have sufficient + permissions to perform the operation. + + <dt><code>STATUS_FAILURE = 4</code> + <dd>is a generic catch-all error message; it should be returned if an + error occurs for which there is no more specific error code. + + <dt><code>STATUS_BAD_MESSAGE = 5</code> + <dd>may be returned if a badly formatted packet or protocol + incompatibility is detected. + + <dt><code>STATUS_NO_CONNECTION = 6</code> + <dd>is a pseudo-error which indicates that the client has no + connection to the server (it can only be generated locally by the + client, and MUST NOT be returned by servers). + + <dt><code>STATUS_CONNECTION_LOST = 7</code> + <dd>is a pseudo-error which indicates that the connection to the + server has been lost (it can only be generated locally by the + client, and MUST NOT be returned by servers). + + <dt><code>STATUS_OP_UNSUPPORTED = 8</code> + <dd>indicates that an attempt was made to perform an operation which + is not supported for the server (it may be generated locally by + the client if e.g. the version number exchange indicates that a + required feature is not supported by the server, or it may be + returned by the server if the server does not implement an + operation). +</dl> + +<h2><a name='Cmds'>Commands</a></h2> + +<h3><a name='CmdOpen'>open</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • open • <i><string: file name></i> • <i><int: mode></i> • <i><file attributes></i> • +</font></b></pre> + +<p>The command opens or creates a file on a remote system. If mode contains O_CREAT then new file is created, otherwise exsting +file is opened. If the file is created, file attributes is looked up for UID, GID and permissions. If no attribute value is found in +the command parameters, a default value is used.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><file handle></i> • + +<i><file handle></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><string></i> +</font></b></pre> + +<p>On success, the replay contains open file handle. The handle is encoded as a string of characters. Client should never try +to decode the string, but should use it as is to issue further file access commands. Client should close the file when it is +not needed any more. Server should close all files that were left open after client connection was closed ot terminated.</p> + +<h3><a name='CmdClose'>close</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • close • <i><string: file handle></i> • +</font></b></pre> + +<p>The command closes a handle, which was open by 'open' or 'opendir' commands.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdRead'>read</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • read • <i><string: file handle></i> • <i><int: offset></i> • <i><int: size></i> • +</font></b></pre> + +<p>The command reads bytes from an open file. +In response to this request, the server will read as many bytes as it +can from the file (up to `len'), and return them in a byte array. +If an error occurs or EOF is encountered, the server may return +fewer bytes then requested. Replay argument 'error report' +will be not null in case of error, and argument 'eof' will be +true in case of EOF. For normal disk files, it is guaranteed +that this will read the specified number of bytes, or up to end of file +or error. For e.g. device files this may return fewer bytes than requested.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><string: data></i> • <i><error report></i> • <i><boolean: eof></i> • +</font></b></pre> + +<p><i><string: data></i> is Base64 encoded byte array of file data. Number of bytes is determined by the string length. +'eof' is true when 'data' contains all available bytes up to the end of the file.</p> + +<h3><a name='CmdWrite'>write</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • write • <i><string: file handle></i> • <i><int: offset></i> • <i><string: data></i> • +</font></b></pre> + +<p>The command writes bytes into an open file. +The write will extend the file if writing beyond the end of the file. +It is legal to write way beyond the end of the file; the semantics +are to write zeroes from the end of the file to the specified offset +and then the data. <i><string: data></i> is Base64 encoded array of data bytes.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdStat'>stat</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • stat • <i><string: file name></i> • +</font></b></pre> + +<p>The command retrieves file attributes.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><file attributes></i> • +</font></b></pre> + +<h3><a name='CmdLStat'>lstat</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • lstat • <i><string: file name></i> • +</font></b></pre> + +<p>The command retrieves file attributes. +Unlike 'stat', 'lstat' does not follow symbolic links.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><file attributes></i> • +</font></b></pre> + +<h3><a name='CmdFStat'>fstat</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • fstat • <i><string: file handle></i> • +</font></b></pre> + +<p>The command retrieves file attributes for an open file (identified by the file handle).</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><file attributes></i> • +</font></b></pre> + +<h3><a name='CmdSetStat'>setstat</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • setstat • <i><string: file name></i> • <i><file attributes></i> • +</font></b></pre> + +<p>The command sets file attributes. +This request is used for operations such as changing the ownership, +permissions or access times, as well as for truncating a file. +An error will be returned if the specified file system object does +not exist or the user does not have sufficient rights to modify the +specified attributes.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdFSetStat'>fsetstat</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • fsetstat • <i><string: file handle></i> • <i><file attributes></i> • +</font></b></pre> + +<p>The command sets file attributes for an open file (identified by the file handle). +This request is used for operations such as changing the ownership, +permissions or access times, as well as for truncating a file.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdOpenDir'>opendir</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • opendir • <i><string: path></i> • +</font></b></pre> + +<p>The command opens a directory for reading. +Once the directory has been successfully opened, files (and +directories) contained in it can be listed using 'readdir' requests. +When the client no longer wishes to read more names from the +directory, it SHOULD call 'close' for the handle. The handle +should be closed regardless of whether a read errors have occurred or not.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><file handle></i> • +</font></b></pre> + +<h3><a name='CmdReadDir'>readdir</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • readdir • <i><string: file handle></i> • +</font></b></pre> + +<p>The command returns one +or more file names with full file attributes for each file. The +client should call 'readdir' repeatedly until it has found the +file it is looking for or until the server responds with a +message indicating an error or end of file. The client should then +close the handle using the 'close' request. +Note: directory entries "." and ".." are NOT included into readdir() +response.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><array of directory entries></i> • <i><error report></i> • <i><boolean: eof></i> • + +<i><array of directory entries></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><directory entry list></i> ] + +<i><directory entry list></i> + <font face=Wingdings>Ø</font> <i><directory entry></i> + <font face=Wingdings>Ø</font> <i><directory entry list></i> , <i><directory entry></i> + +<i><directory entry></i> + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p>Directory entry attributes are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"FileName" : <i><string></i></font></b></code> + - a file name being returned, it will be a relative name within the directory, + without any path components. + <li><code><b><font face="Courier New" size=2 color=#333399>"LongName" : <i><string></i></font></b></code> + - a human readable, expanded format for the file name, similar to what + is returned by "ls -l" on Unix systems + <li><code><b><font face="Courier New" size=2 color=#333399>"Attrs" : <i><file attributes></i></font></b></code> + - the attributes of the file as described in Section <a href='#FileAttributes'>File Attributes</a>. +</ul> + +<h3><a name='CmdMkDir'>mkdir</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • mkdir • <i><string: directory path></i> • <i><file attributes></i> • +</font></b></pre> + +<p>The command creates a directory on the server. +<i><string: directory path></i> specifies the directory to be created. +<i><file attributes></i> specifies new directory attributes.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdRmDir'>rmdir</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • rmdir • <i><string: directory path></i> • +</font></b></pre> + +<p>The command removes a directory. +An error will be returned if no directory +with the specified path exists, or if the specified directory is not +empty, or if the path specified a file system object other than a +directory. <i><string: directory path></i> - specifies the directory to be removed.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdRoots'>roots</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • roots • +</font></b></pre> + +<p>The command retrieves file system roots - top level file system objects. +UNIX file system can report just one root with path "/". Other types of systems +can have more the one root. For example, Windows server can return multiple roots: +one per disc (e.g. "/C:/", "/D:/", etc.). Note: even Windows implementation of +the service must use forward slash as directory separator, and must start +absolute path with "/". Server should implement proper translation of +protocol file names to OS native names and back.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of directory entries></i> • +</font></b></pre> + +<h3><a name='CmdRemove'>remove</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • remove • <i><string: path></i> • +</font></b></pre> + +<p>The command removes a file or symbolic link. +This request cannot be used to remove directories.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdRealPath'>realpath</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • realpath • <i><string: path></i> • +</font></b></pre> + +<p>The command canonicalizes any given path name to an absolute path. +This is useful for converting path names containing ".." components or +relative pathnames without a leading slash into absolute paths.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><string: path></i> • +</font></b></pre> + +<h3><a name='CmdRename'>rename</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • rename • <i><string: old path></i> • <i><string: new path></i> • +</font></b></pre> + +<p>The command renames a file. +It is an error if there already exists a file +with the name specified by <i><string: new path></i>. The server may also fail rename +requests in other situations, for example if <i><string: old path></i> and <i><string: new path></i> +point to different file systems on the server.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdReadLink'>readlink</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • readlink • <i><string: link path></i> • +</font></b></pre> + +<p>The command reads the target of a symbolic link. +<i><string: link path></i> specifies the path name of the symbolic link to be read.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><string: path></i> • +</font></b></pre> + +<h3><a name='CmdSymLink'>symlink</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • symlink • <i><string: link path></i> • <i><string: target path></i> • +</font></b></pre> + +<p>The command creates a symbolic link on the server. +<i><string: link path></i> specifies the path name of the symbolic link to be created. +<i><string: target path></i> specifies the target of the symbolic link.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdCopy'>copy</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • copy • <i><string: source path></i> • <i><string: destination path></i> • + <i><boolean: copy permissions></i> • <i><boolean: copy ownership></i> • +</font></b></pre> + +<p>The command copies a file on remote system. +<i><string: source path></i> specifies the path name of the file to be copied. +<i><string: destination path></i> specifies destination file name. +If <i><boolean: copy permissions></i> is true then copy source file permissions. +If <i><boolean: copy ownership></i> is true then copy source file UID and GID.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdUser'>user</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • FileSystem • user • +</font></b></pre> + +<p>The command retrieves information about user account, which is used by server +to access file system on behalf of the client.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><int: real UID></i> • <i><int: effective UID></i> • + <i><int: real GID></i> • <i><int: effective GID></i> • <i><string: home directory></i> • +</font></b></pre> + +<h2><a name='API'>API</a></h2> + +<pre> +<font color=#3F5FBF>/** + * File System service provides file transfer (and more generally file + * system access) functionality in TCF. The service design is + * derived from SSH File Transfer Protocol specifications. + */</font> +<font color=#7F0055>public interface</font> IFileSystem <font color=#7F0055>extends</font> IService { + + <font color=#3F5FBF>/** + * Service name. + */</font> + <font color=#7F0055>static final</font> String NAME = "FileSystem"; + + <font color=#3F5FBF>/** + * Flags to be used with open() method. + */</font> + <font color=#7F0055>static final int</font> + + <font color=#3F5FBF>/** + * Open the file for reading. + */</font> + O_READ = 0x00000001, + + <font color=#3F5FBF>/** + * Open the file for writing. If both this and O_READ are + * specified, the file is opened for both reading and writing. + */</font> + O_WRITE = 0x00000002, + + <font color=#3F5FBF>/** + * Force all writes to append data at the end of the file. + */</font> + O_APPEND = 0x00000004, + + <font color=#3F5FBF>/** + * If this flag is specified, then a new file will be created if one + * does not already exist (if O_TRUNC is specified, the new file will + * be truncated to zero length if it previously exists). + */</font> + O_CREAT = 0x00000008, + + <font color=#3F5FBF>/** + * Forces an existing file with the same name to be truncated to zero + * length when creating a file by specifying O_CREAT. + * O_CREAT MUST also be specified if this flag is used. + */</font> + O_TRUNC = 0x00000010, + + <font color=#3F5FBF>/** + * Causes the request to fail if the named file already exists. + * O_CREAT MUST also be specified if this flag is used. + */</font> + O_EXCL = 0x00000020; + + <font color=#3F5FBF>/** + * Flags to be used together with FileAttrs. + * The flags specify which of the fields are present. Those fields + * for which the corresponding flag is not set are not present (not + * included in the message). + */</font> + <font color=#7F0055>static final int</font> + ATTR_SIZE = 0x00000001, + ATTR_UIDGID = 0x00000002, + ATTR_PERMISSIONS = 0x00000004, + ATTR_ACMODTIME = 0x00000008; + + <font color=#3F5FBF>/** + * FileAttrs is used both when returning file attributes from + * the server and when sending file attributes to the server. When + * sending it to the server, the flags field specifies which attributes + * are included, and the server will use default values for the + * remaining attributes (or will not modify the values of remaining + * attributes). When receiving attributes from the server, the flags + * specify which attributes are included in the returned data. The + * server normally returns all attributes it knows about. + */</font> + <font color=#7F0055>final static class</font> FileAttrs { + + <font color=#3F5FBF>/** + * The `flags' specify which of the fields are present. + */</font> + <font color=#7F0055>public final int</font> flags; + + <font color=#3F5FBF>/** + * The `size' field specifies the size of the file in bytes. + */</font> + <font color=#7F0055>public final long</font> size; + + <font color=#3F5FBF>/** + * The `uid' and `gid' fields contain numeric Unix-like user and group + * identifiers, respectively. + */</font> + <font color=#7F0055>public final int</font> uid; + <font color=#7F0055>public final int</font> gid; + + <font color=#3F5FBF>/** + * The `permissions' field contains a bit mask of file permissions as + * defined by posix [1]. + */</font> + <font color=#7F0055>public final int</font> permissions; + + <font color=#3F5FBF>/** + * The `atime' and `mtime' contain the access and modification times of + * the files, respectively. They are represented as milliseconds from + * midnight Jan 1, 1970 in UTC. + */</font> + <font color=#7F0055>public final long</font> atime; + <font color=#7F0055>public final long</font> mtime; + + <font color=#3F5FBF>/** + * Additional (non-standard) attributes. + */</font> + <font color=#7F0055>public final</font> Map<String,Object> attributes; + + <font color=#7F0055>public</font> FileAttrs(<font color=#7F0055>int</font> flags, <font color=#7F0055>long</font> size, <font color=#7F0055>int</font> uid, <font color=#7F0055>int</font> gid, + <font color=#7F0055>int</font> permissions, <font color=#7F0055>long</font> atime, <font color=#7F0055>long</font> mtime, Map<String,Object> attributes); + + <font color=#3F5FBF>/** + * Determines if the file system object is a file on the remote file system. + * + * @return true if and only if the object on the remote system can be considered to have "contents" that + * have the potential to be read and written as a byte stream. + */</font> + <font color=#7F0055>public boolean</font> isFile(); + + <font color=#3F5FBF>/** + * Determines if the file system object is a directory on the remote file system. + * + * @return true if and only if the object on the remote system is a directory. + * That is, it contains entries that can be interpreted as other files. + */</font> + <font color=#7F0055>public boolean</font> isDirectory(); + } + + <font color=#3F5FBF>/** + * The following flags are defined for the 'permissions' field: + */</font> + <font color=#7F0055>static final int</font> + S_IFMT = 0170000, // bitmask for the file type bitfields + S_IFSOCK = 0140000, // socket + S_IFLNK = 0120000, // symbolic link + S_IFREG = 0100000, // regular file + S_IFBLK = 0060000, // block device + S_IFDIR = 0040000, // directory + S_IFCHR = 0020000, // character device + S_IFIFO = 0010000, // fifo + S_ISUID = 0004000, // set UID bit + S_ISGID = 0002000, // set GID bit (see below) + S_ISVTX = 0001000, // sticky bit (see below) + S_IRWXU = 00700, // mask for file owner permissions + S_IRUSR = 00400, // owner has read permission + S_IWUSR = 00200, // owner has write permission + S_IXUSR = 00100, // owner has execute permission + S_IRWXG = 00070, // mask for group permissions + S_IRGRP = 00040, // group has read permission + S_IWGRP = 00020, // group has write permission + S_IXGRP = 00010, // group has execute permission + S_IRWXO = 00007, // mask for permissions for others (not in group) + S_IROTH = 00004, // others have read permission + S_IWOTH = 00002, // others have write permisson + S_IXOTH = 00001; // others have execute permission + + <font color=#7F0055>final static class</font> DirEntry { + <font color=#3F5FBF>/** + * `filename' is a file name being returned. It is a relative name within + * the directory, without any path components; + */</font> + <font color=#7F0055>public final</font> String filename; + + <font color=#3F5FBF>/** + * `longname' is an expanded format for the file name, similar to what + * is returned by "ls -l" on Unix systems. + * The format of the `longname' field is unspecified by this protocol. + * It MUST be suitable for use in the output of a directory listing + * command (in fact, the recommended operation for a directory listing + * command is to simply display this data). However, clients SHOULD NOT + * attempt to parse the longname field for file attributes; they SHOULD + * use the attrs field instead. + */</font> + <font color=#7F0055>public final</font> String longname; + + <font color=#3F5FBF>/** + * `attrs' is the attributes of the file. + */</font> + <font color=#7F0055>public final</font> FileAttrs attrs; + + <font color=#7F0055>public</font> DirEntry(String filename, String longname, FileAttrs attrs); + } + + <font color=#3F5FBF>/** + * Opaque representation of open file handle. + * Note: open file handle can be used only with service instance that + * created the handle. + */</font> + <font color=#7F0055>interface</font> IFileHandle { + IFileSystem getService(); + } + + <font color=#3F5FBF>/** + * Status codes. + */</font> + <font color=#7F0055>static final int</font> + + <font color=#3F5FBF>/** + * Indicates successful completion of the operation. + */</font> + STATUS_OK = 0, + + <font color=#3F5FBF>/** + * Indicates end-of-file condition; for read() it means that no + * more data is available in the file, and for readdir() it + * indicates that no more files are contained in the directory. + */</font> + STATUS_EOF = 1, + + <font color=#3F5FBF>/** + * This code is returned when a reference is made to a file which + * should exist but doesn't. + */</font> + STATUS_NO_SUCH_FILE = 2, + + <font color=#3F5FBF>/** + * is returned when the authenticated user does not have sufficient + * permissions to perform the operation. + */</font> + STATUS_PERMISSION_DENIED = 3, + + <font color=#3F5FBF>/** + * is a generic catch-all error message; it should be returned if an + * error occurs for which there is no more specific error code. + * + */</font> + STATUS_FAILURE = 4, + + <font color=#3F5FBF>/** + * may be returned if a badly formatted packet or protocol + * incompatibility is detected. + */</font> + STATUS_BAD_MESSAGE = 5, + + <font color=#3F5FBF>/** + * is a pseudo-error which indicates that the client has no + * connection to the server (it can only be generated locally by the + * client, and MUST NOT be returned by servers). + */</font> + STATUS_NO_CONNECTION = 6, + + <font color=#3F5FBF>/** + * is a pseudo-error which indicates that the connection to the + * server has been lost (it can only be generated locally by the + * client, and MUST NOT be returned by servers). + */</font> + STATUS_CONNECTION_LOST = 7, + + <font color=#3F5FBF>/** + * indicates that an attempt was made to perform an operation which + * is not supported for the server (it may be generated locally by + * the client if e.g. the version number exchange indicates that a + * required feature is not supported by the server, or it may be + * returned by the server if the server does not implement an + * operation). + */</font> + STATUS_OP_UNSUPPORTED = 8; + + <font color=#7F0055>abstract static class</font> FileSystemException extends IOException { + + <font color=#7F0055>protected</font> FileSystemException(String message); + + <font color=#7F0055>protected</font> FileSystemException(Exception x) + + <font color=#7F0055>public abstract int</font> getStatus(); + } + + <font color=#3F5FBF>/** + * Open or create a file on a remote system. + * + * @param file_name specifies the file name. See 'File Names' for more information. + * @param flags is a bit mask of O_* flags. + * @param attrs specifies the initial attributes for the file. + * Default values will be used for those attributes that are not specified. + * @param done is call back object. + * @return pending command handle. + */</font> + IToken open(String file_name, <font color=#7F0055>int</font> flags, FileAttrs attrs, DoneOpen done); + + <font color=#7F0055>interface</font> DoneOpen { + <font color=#7F0055>void</font> doneOpen(IToken token, FileSystemException error, IFileHandle handle); + } + + <font color=#3F5FBF>/** + * Close a file on a remote system. + * + * @param handle is a handle previously returned in the response to + * open() or opendir(). + * @param done is call back object. + * @return pending command handle. + */</font> + IToken close(IFileHandle handle, DoneClose done); + + <font color=#7F0055>interface</font> DoneClose { + <font color=#7F0055>void</font> doneClose(IToken token, FileSystemException error); + } + + <font color=#3F5FBF>/** + * Read bytes from an open file. + * In response to this request, the server will read as many bytes as it + * can from the file (up to `len'), and return them in a byte array. + * If an error occurs or EOF is encountered, the server may return + * fewer bytes then requested. Call back method doneRead() argument 'error' + * will be not null in case of error, and argument 'eof' will be + * true in case of EOF. For normal disk files, it is guaranteed + * that this will read the specified number of bytes, or up to end of file + * or error. For e.g. device files this may return fewer bytes than requested. + * + * @param handle is an open file handle returned by open(). + * @param offset is the offset (in bytes) relative + * to the beginning of the file from where to start reading. + * @param len is the maximum number of bytes to read. + * @param done is call back object. + * @return pending command handle. + */</font> + IToken read(IFileHandle handle, long offset, <font color=#7F0055>int</font> len, DoneRead done); + + <font color=#7F0055>interface</font> DoneRead { + <font color=#7F0055>void</font> doneRead(IToken token, FileSystemException error, byte[] data, boolean eof); + } + + <font color=#3F5FBF>/** + * Write bytes into an open file. + * The write will extend the file if writing beyond the end of the file. + * It is legal to write way beyond the end of the file; the semantics + * are to write zeroes from the end of the file to the specified offset + * and then the data. + * + * @param handle is an open file handle returned by open(). + * @param offset is the offset (in bytes) relative + * to the beginning of the file from where to start writing. + * @param data is byte array that contains data for writing. + * @param data_pos if offset in 'data' of first byte to write. + * @param data_size is the number of bytes to write. + * @param done is call back object. + * @return pending command handle. + */</font> + IToken write(IFileHandle handle, long offset, + byte[] data, <font color=#7F0055>int</font> data_pos, <font color=#7F0055>int</font> data_size, DoneWrite done); + + <font color=#7F0055>interface</font> DoneWrite { + <font color=#7F0055>void</font> doneWrite(IToken token, FileSystemException error); + } + + <font color=#3F5FBF>/** + * Retrieve file attributes. + * + * @param path - specifies the file system object for which + * status is to be returned. + * @param done is call back object. + * @return pending command handle. + */</font> + IToken stat(String path, DoneStat done); + + <font color=#3F5FBF>/** + * Retrieve file attributes. + * Unlike 'stat()', 'lstat()' does not follow symbolic links. + * + * @param path - specifies the file system object for which + * status is to be returned. + * @param done is call back object. + * @return pending command handle. + */</font> + IToken lstat(String path, DoneStat done); + + <font color=#3F5FBF>/** + * Retrieve file attributes for an open file (identified by the file handle). + * + * @param handle is a file handle returned by 'open()'. + * @param done is call back object. + * @return pending command handle. + */</font> + IToken fstat(IFileHandle handle, DoneStat done); + + <font color=#7F0055>interface</font> DoneStat { + <font color=#7F0055>void</font> doneStat(IToken token, FileSystemException error, FileAttrs attrs); + } + + <font color=#3F5FBF>/** + * Set file attributes. + * This request is used for operations such as changing the ownership, + * permissions or access times, as well as for truncating a file. + * An error will be returned if the specified file system object does + * not exist or the user does not have sufficient rights to modify the + * specified attributes. + * + * @param path specifies the file system object (e.g. file or directory) + * whose attributes are to be modified. + * @param attrs specifies the modifications to be made to file attributes. + * @param done is call back object. + * @return pending command handle. + */</font> + IToken setstat(String path, FileAttrs attrs, DoneSetStat done); + + <font color=#3F5FBF>/** + * Set file attributes for an open file (identified by the file handle). + * This request is used for operations such as changing the ownership, + * permissions or access times, as well as for truncating a file. + * + * @param handle is a file handle returned by 'open()'. + * @param attrs specifies the modifications to be made to file attributes. + * @param done is call back object. + * @return pending command handle. + */</font> + IToken fsetstat(IFileHandle handle, FileAttrs attrs, DoneSetStat done); + + <font color=#7F0055>interface</font> DoneSetStat { + <font color=#7F0055>void</font> doneSetStat(IToken token, FileSystemException error); + } + + <font color=#3F5FBF>/** + * The opendir() command opens a directory for reading. + * Once the directory has been successfully opened, files (and + * directories) contained in it can be listed using readdir() requests. + * When the client no longer wishes to read more names from the + * directory, it SHOULD call close() for the handle. The handle + * should be closed regardless of whether an error has occurred or not. + + * @param path - name of the directory to be listed (without any trailing slash). + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken opendir(String path, DoneOpen done); + + <font color=#3F5FBF>/** + * The files in a directory can be listed using the opendir() and + * readdir() requests. Each readdir() request returns one + * or more file names with full file attributes for each file. The + * client should call readdir() repeatedly until it has found the + * file it is looking for or until the server responds with a + * message indicating an error or end of file. The client should then + * close the handle using the close() request. + * Note: directory entries "." and ".." are NOT included into readdir() + * response. + * @param handle - file handle created by opendir() + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken readdir(IFileHandle handle, DoneReadDir done); + + <font color=#7F0055>interface</font> DoneReadDir { + <font color=#7F0055>void</font> doneReadDir(IToken token, FileSystemException error, DirEntry[] entries, boolean eof); + } + + <font color=#3F5FBF>/** + * Create a directory on the server. + * + * @param path - specifies the directory to be created. + * @param attrs - new directory attributes. + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken mkdir(String path, FileAttrs attrs, DoneMkDir done); + + <font color=#7F0055>interface</font> DoneMkDir { + <font color=#7F0055>void</font> doneMkDir(IToken token, FileSystemException error); + } + + <font color=#3F5FBF>/** + * Remove a directory. + * An error will be returned if no directory + * with the specified path exists, or if the specified directory is not + * empty, or if the path specified a file system object other than a + * directory. + * + * @param path - specifies the directory to be removed. + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken rmdir(String path, DoneRemove done); + + <font color=#7F0055>interface</font> DoneRemove { + <font color=#7F0055>void</font> doneRemove(IToken token, FileSystemException error); + } + + <font color=#3F5FBF>/** + * Retrieve file system roots - top level file system objects. + * UNIX file system can report just one root with path "/". Other types of systems + * can have more the one root. For example, Windows server can return multiple roots: + * one per disc (e.g. "/C:/", "/D:/", etc.). Note: even Windows implementation of + * the service must use forward slash as directory separator, and must start + * absolute path with "/". Server should implement proper translation of + * protocol file names to OS native names and back. + * + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken roots(DoneRoots done); + + <font color=#7F0055>interface</font> DoneRoots { + <font color=#7F0055>void</font> doneRoots(IToken token, FileSystemException error, DirEntry[] entries); + } + + <font color=#3F5FBF>/** + * Remove a file or symbolic link. + * This request cannot be used to remove directories. + * + * @param file_name is the name of the file to be removed. + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken remove(String file_name, DoneRemove done); + + <font color=#3F5FBF>/** + * Canonicalize any given path name to an absolute path. + * This is useful for converting path names containing ".." components or + * relative pathnames without a leading slash into absolute paths. + * + * @param path specifies the path name to be canonicalized. + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken realpath(String path, DoneRealPath done); + + <font color=#7F0055>interface</font> DoneRealPath { + <font color=#7F0055>void</font> doneRealPath(IToken token, FileSystemException error, String path); + } + + <font color=#3F5FBF>/** + * Rename a file. + * It is an error if there already exists a file + * with the name specified by 'new_path'. The server may also fail rename + * requests in other situations, for example if `old_path' and `new_path' + * point to different file systems on the server. + * + * @param old_path is the name of an existing file or directory. + * @param new_path is the new name for the file or directory. + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken rename(String old_path, String new_path, DoneRename done); + + <font color=#7F0055>interface</font> DoneRename { + <font color=#7F0055>void</font> doneRename(IToken token, FileSystemException error); + } + + <font color=#3F5FBF>/** + * Read the target of a symbolic link. + * + * @param path specifies the path name of the symbolic link to be read. + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken readlink(String path, DoneReadLink done); + + <font color=#7F0055>interface</font> DoneReadLink { + <font color=#7F0055>void</font> doneReadLink(IToken token, FileSystemException error, String path); + } + + <font color=#3F5FBF>/** + * Create a symbolic link on the server. + * + * @param link_path specifies the path name of the symbolic link to be created. + * @param target_path specifies the target of the symbolic link. + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken symlink(String link_path, String target_path, DoneSymLink done); + + <font color=#7F0055>interface</font> DoneSymLink { + <font color=#7F0055>void</font> doneSymLink(IToken token, FileSystemException error); + } + + <font color=#3F5FBF>/** + * Copy a file on remote system. + * + * @param src_path specifies the path name of the file to be copied. + * @param dst_path specifies destination file name. + * @param copy_permissions - if true then copy source file permissions. + * @param copy_ownership - if true then copy source file UID and GID. + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken copy(String src_path, String dst_path, + boolean copy_permissions, boolean copy_ownership, DoneCopy done); + + <font color=#7F0055>interface</font> DoneCopy { + <font color=#7F0055>void</font> doneCopy(IToken token, FileSystemException error); + } + + <font color=#3F5FBF>/** + * Retrieve information about user account, which is used by server + * to access file system on behalf of the client. + * + * @param done - result call back object. + * @return pending command handle. + */</font> + IToken user(DoneUser done); + + <font color=#7F0055>interface</font> DoneUser { + <font color=#7F0055>void</font> doneUser(IToken token, FileSystemException error, + <font color=#7F0055>int</font> real_uid, <font color=#7F0055>int</font> effective_uid, <font color=#7F0055>int</font> real_gid, <font color=#7F0055>int</font> effective_gid, + String home); + } +} +</pre> + +</body> +</html> + + diff --git a/docs/TCF Service - Memory.html b/docs/TCF Service - Memory.html new file mode 100644 index 000000000..afca975ea --- /dev/null +++ b/docs/TCF Service - Memory.html @@ -0,0 +1,456 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services - Memory</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services - Memory</h1> + +<ul> + <li><a href='#Cmds'>Commands</a> + <ul> + <li><a href='#CmdGetContext'>Get Context</a> + <li><a href='#CmdGetChildren'>Get Children</a> + <li><a href='#CmdSetMemory'>Set Memory</a> + <li><a href='#CmdGetMemory'>Get Memory</a> + <li><a href='#CmdFillMemory'>Fill Memory</a> + </ul> + <li><a href='#Events'>Events</a> + <li><a href='#API'>API</a> +</ul> + +<h1>Memory Service</h1> + +<p>The service provides basic operations to read/write memory on a target. Command +and event parameters are encoded as zero terminated <a href='TCF Specification.html#JSON'>JSON</a> strings.</p> + +<p>The service uses standard format for error reports, +see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p> + +<p>A single memory access can succeed for some addresses and fail for others. In such +situation result message can contain partially valid data. Array of error addresses, +in addition to error report, describes data validity on per byte basis:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><array of error addresses></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ <i><error address list></i> ] + +<i><error address list></i> + <font face=Wingdings>Ø</font> <i><error address></i> + <font face=Wingdings>Ø</font> <i><error address list></i> , <i><error address></i> + +<i><error address></i> + <font face=Wingdings>Ø</font> { "addr" : <i><int: range starting address></i> , "size" : <i><int: range length in bytes></i> , "stat" : <i><int: status code></i> , "msg" : <i><error description></i> } +</font></b></pre> + +<p>If there is no entry in error addresses array for a data byte, then status of such +byte is defined by main error report.</p> + +<p>Status code is bitwise or of status flags:</p> +<dl> + <dt><code><b>BYTE_VALID = 0x00</b></code> <dd>no error for this byte + <dt><code><b>BYTE_UNKNOWN = 0x01</b></code> <dd>status is unknown + <dt><code><b>BYTE_INVALID = 0x02</b></code> <dd>byte value in invalid, error message describes the problem + <dt><code><b>BYTE_CANNOT_READ = 0x04</b></code> <dd>cannot read the byte + <dt><code><b>BYTE_CANNOT_WRITE = 0x08</b></code> <dd>cannot write the byte +</dl> + +<h2><a name='Cmds'>Commands</a></h2> + +<h3><a name='CmdGetContext'>Get Context</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Memory • getContext • <i><string: context ID></i> • +</font></b></pre> + +<p>The command retrieves context info for given context ID. A context corresponds to an +execution thread, process, address space, etc. Exact +meaning of a context depends on the target. Target agent should define contexts hierarchy +that is:</p> + +<ul type='disc'> + <li>Sufficient to resolve possible ambiguity of a memory address; + + <li>Adequately reflects target memory management strategy; + + <li>Intuitive to a user. +</ul> + +<p>For traditional OS, like UNIX, memory access context can be one of:</p> + +<ul type='disc'> + <li>Kernel address space; + + <li>A process. +</ul> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><context data></i> • + +<i><context data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p>Context data object should, at least, contain member +<b><font face="Courier New" size=2 color=#333399>"ID" : <i><string>.</i></font></b> +Context data is expected to be cached by clients. +Service sends contextChanged event to notify changes in context data.</p> + +<p>Predefined memory context properties are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b></code> + - ID of the context, same as getContext command argument. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ParentID" : <i><string></i></font></b></code> + - ID of a parent context. + + <li><code><b><font face="Courier New" size=2 color=#333399>"BigEndian" : <i><boolean></i></font></b></code> + - true if memory is big-endian. + + <li><code><b><font face="Courier New" size=2 color=#333399>"AddressSize" : <i><int></i></font></b></code> + - size of memory address in bytes. +</ul> + +<h3><a name='CmdGetChildren'>Get Children</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Memory • getChildren • <i><string: parent context ID></i> • +</font></b></pre> + +<p>The command requests a list of contexts available for memory access commands.</p> + +<p>Parent context ID can be null – to retrieve top level of the hierarchy, can be one +of context IDs retrieved by previous getChildren commands, or it can be obtained from another service. +Contexts hierarchy can be simple plain list or it can form a tree. It is up to target agent developers to +choose layout that is most descriptive for a given target.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of context IDs></i> •<i></i> + +<i><array of context IDs></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><context ID list></i> ] + +<i><context ID list></i> + <font face=Wingdings>Ø</font> <i><string: context ID></i> + <font face=Wingdings>Ø</font> <i><context ID list></i> , <i><string: context ID></i> + +</font></b></pre> + +<h3><a name='CmdSetMemory'>Set Memory</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <token> • Memory • set • + <i><string: context ID></i> • <i><int: address></i> • <i><int: word size></i> • + <i><int: byte count></i> • <i><int: mode></i> • <i><string: BASE64 encoded byte array></i> • +</font></b></pre> + +<p>Writes data bytes at given address in memory, "word size" bytes at a time. Address +should be aligned by "word size". Context ID must be one returned by getContexts. +Mode is logical OR of any combination of:</p> + +<ul type='disc'> + <li>0x1 – continue on error (like bus error or page fault) + + <li>0x2 – verify data after writing by reading back and compare +</ul> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of error addresses></i> • +</font></b></pre> + +<p>Error report provides integer error code and a short, human readable explanation +of error. Error addresses, when present, let client know which bytes of data failed +to be written into memory.</p> + +<h3><a name='CmdGetMemory'>Get Memory</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <token> • Memory • get • + <i><string: context ID></i> • <i><int: address></i> • <i><int: word size></i> • + <i><int: byte count></i> • <i><int: mode></i> • +</font></b></pre> + +<p>Reads data bytes at given address in memory, "word size" bytes at a time. Address +should be aligned by "word size". Context ID must be one returned by getContexts. +Mode is logical OR of any combination of:</p> + +<ul type='disc'> + <li>0x1 – continue on error (like bus error or page fault) + + <li>0x2 – verify data after reading by re-reading and compare +</ul> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><string: BASE64 encoded byte array></i> • <i><error report></i> • <i><array of error addresses></i> • +</font></b></pre> + +<p>Error report provides integer error code and a short, human readable explanation +of error. Error addresses, when present, let client know which bytes of data failed +to be retrieved from memory.</p> + +<h3><a name='CmdFillMemory'>Fill Memory</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <token> • Memory • fill • + <i><string: context ID></i> • <i><int: address></i> • <i><int: word size></i> • + <int: byte count> • <i><int: mode></i> • <i><array: array of pattern bytes></i> • +</font></b></pre> + +<p>Writes pattern bytes at given address in memory, "word size" bytes at a time. Address +should be aligned by "word size". If "byte count" is bigger then pattern size, then +pattern is repeated necessary number of times. Context ID must be one returned by +getContexts. Mode is logical OR of any combination of:</p> + +<ul type='disc'> + <li>0x1 – continue on error (like bus error or page fault) + + <li>0x2 – verify data after writing by reading back and compare +</ul> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of error addresses></i> • +</font></b></pre> + +<p>Error report provides integer error code and a short, human readable explanation +of error. Error addresses, when present, let client know which bytes of data failed +to be written into memory.</p> + +<h2><a name='Events'>Events</a></h2> + +<p>Memory service broadcasts notification events when memory contexts are added, removed +or changed, and when memory content is altered by "set" or "fill" commands.</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +E • Memory • contextAdded • <i><array of context data></i> • +E • Memory • contextChanged • <i><array of context data></i> • +E • Memory • contextRemoved • <i><array of context IDs></i> • +E • Memory • memoryChanged • <i><string: context ID></i> • <i><array of address ranges></i> • + +<i><array of context data></i> <font face="Times New Roman" size=3>- see Get Contexts command.</font> + +<i><array of context IDs></i> + <font face=Wingdings>Ø</font> [ <i><context ID list></i> ] + +<i><context ID list></i> + <font face=Wingdings>Ø</font> <i><string: context ID></i> + <font face=Wingdings>Ø</font> <i><context ID list></i> , <i><string: context ID></i> + +<i><array of address ranges></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ <i><address ranges list></i> ] + +<i><address ranges list></i> + <font face=Wingdings>Ø</font> <i><address range></i> + <font face=Wingdings>Ø</font> <i><address ranges list></i> , <i><address range></i> + +<i><address range></i> + <font face=Wingdings>Ø</font> { "addr" : <i><int: range starting address></i> , "size" : <i><int: range length in bytes></i> } +</font></b></pre> + +<h2><a name='API'>API</a></h2> + +<pre> +<font color=#3F5FBF>/** + * IMemory service provides basic operations to read/write memory on a target. + */</font> +<font color=#7F0055>public interface</font> Memory <font color=#7F0055>extends</font> Service { + + <font color=#7F0055>static final</font> String NAME = "Memory"; + + <font color=#3F5FBF>/** + * Retrieve context info for given context ID. + * + * <font color=#7F9FBF>@param</font> id – context ID. + * <font color=#7F9FBF>@param</font> done - callback interface called when operation is completed. + */</font> + IToken getContext(String id, DoneGetContext done); + + <font color=#3F5FBF>/** + * Client callback interface for getContext(). + */</font> + <font color=#7F0055>interface</font> DoneGetContext { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context – context data. + */</font> + <font color=#7F0055>void</font> doneGetContext(IToken token, Exception error, MemoryContext context); + } + + <font color=#3F5FBF>/** + * Retrieve contexts available for memory commands. + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + * + * <font color=#7F9FBF>@param</font> parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContexts commands. + * <font color=#7F9FBF>@param</font> done - callback interface called when operation is completed. + */</font> + IToken getChildren(String parent_context_id, DoneGetChildren done); + + <font color=#3F5FBF>/** + * Client callback interface for getChildren(). + */</font> + <font color=#7F0055>interface</font> DoneGetChildren { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> contexts – array of available context IDs. + */</font> + <font color=#7F0055>void</font> doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + <font color=#3F5FBF>/** + * Memory access mode: + * Carry on when some of the memory cannot be accessed and + * return MemoryError at the end if any of the bytes + * were not processed correctly. + */</font> + <font color=#7F0055>final static int</font> MODE_CONTINUEONERROR = 0x1; + + <font color=#3F5FBF>/** + * Memory access mode: + * Verify result of memory operations (by reading and comparing). + */</font> + <font color=#7F0055>final static int</font> MODE_VERIFY = 0x2; + + <font color=#7F0055>interface</font> MemoryContext { + + <font color=#3F5FBF>/** + * Retrieve context ID. + * Same as getProperties().get("id") + */</font> + String getID(); + + <font color=#3F5FBF>/** + * Return true if the context has children. + * Same as getProperties().get("has_children") + * Children can be retrieved by getContexts call. + */</font> + <font color=#7F0055>boolean</font> hasChildren(); + + <font color=#3F5FBF>/** + * Retrieve context properties. + */</font> + Map<String,Object> getProperties(); + + <font color=#3F5FBF>/** + * Set target memory. + * If 'word_size' is 0 it means client does not care about word size. + */</font> + <font color=#7F0055>void</font> set(long addr, <font color=#7F0055>int</font> word_size, byte[] buf, + <font color=#7F0055>int</font> offs, <font color=#7F0055>int</font> size, <font color=#7F0055>int</font> mode, DoneMemory done); + + <font color=#3F5FBF>/** + * Read target memory. + */</font> + <font color=#7F0055>void</font> get(long addr, <font color=#7F0055>int</font> word_size, byte[] buf, + <font color=#7F0055>int</font> offs, <font color=#7F0055>int</font> size, <font color=#7F0055>int</font> mode, DoneMemory done); + + <font color=#3F5FBF>/** + * Fill target memory with given pattern. + * 'size' is number of bytes to fill. + */</font> + <font color=#7F0055>void</font> fill(long addr, <font color=#7F0055>int</font> word_size, byte[] value, + <font color=#7F0055>int</font> size, <font color=#7F0055>int</font> mode, DoneMemory done); + + <font color=#3F5FBF>/** + * Client callback interface for set(), get() and fill(). + */</font> + <font color=#7F0055>interface</font> DoneMemory { + <font color=#7F0055>void</font> doneMemory(MemoryError error); + } + } + + <font color=#7F0055>class</font> MemoryError <font color=#7F0055>extends</font> Exception { + } + + <font color=#3F5FBF>/** + * ErrorOffset interface can be implemented by MemoryError object, + * which is returned by get, set and fill commands. + * + * get/set/fill () returns this exception when reading failed + * for some but not all bytes, and MODE_CONTINUEONERROR + * has been set in mode. (For example, when only part of the request + * translates to valid memory addresses.) + * Exception.getMessage can be used for generalized message of the + * possible reasons of partial memory operation. + */</font> + <font color=#7F0055>interface</font> ErrorOffset { + + // Error may have per byte information + <font color=#7F0055>final static int</font> + BYTE_VALID = 0x00, + BYTE_UNKNOWN = 0x01, // e.g. out of range + BYTE_INVALID = 0x02, + BYTE_CANNOT_READ = 0x04, + BYTE_CANNOT_WRITE = 0x08; + + <font color=#7F0055>int</font> getStatus(<font color=#7F0055>int</font> offset); + + <font color=#3F5FBF>/** + * Returns the detail message string about the + * byte associated with specified location. + * <font color=#7F9FBF>@return</font> the detail error message string. + */</font> + String getMessage(<font color=#7F0055>int</font> offset); + + } + + <font color=#7F0055>void</font> addListener(MemoryListener listener); + + <font color=#7F0055>interface</font> MemoryListener { + + <font color=#3F5FBF>/** + * Called when a new memory access context(s) is created. + */</font> + <font color=#7F0055>void</font> contextAdded(Context[] contexts); + + <font color=#3F5FBF>/** + * Called when a new memory access context(s) properties changed. + */</font> + <font color=#7F0055>void</font> contextChanged(Context[] contexts); + + <font color=#3F5FBF>/** + * Called when memory access context(s) is removed. + */</font> + <font color=#7F0055>void</font> contextRemoved(String[] context_ids); + + <font color=#3F5FBF>/** + * Called when target memory content was changed and clients + * need to update themselves. Clients, at least, should invalidate + * corresponding cached memory data. + * Not every change is notified - it is not possible, + * only those, which are not caused by normal execution of the debuggee. + * ‘addr’ and ‘size’ can be null if unknown. + */</font> + <font color=#7F0055>void</font> memoryChanged(String context_id, + long[] addr, long[] size); + } +} +</pre> + +</body> +</html> diff --git a/docs/TCF Service - Processes.html b/docs/TCF Service - Processes.html new file mode 100644 index 000000000..dbb3f0309 --- /dev/null +++ b/docs/TCF Service - Processes.html @@ -0,0 +1,381 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services - Processes</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services - Processes</h1> + +<ul> + <li><a href='#Cmds'>Commands</a> + <ul> + <li><a href='#CmdGetContext'>Get Context</a> + <li><a href='#CmdGetChildren'>Get Children</a> + <li><a href='#CmdAttach'>Attach</a> + <li><a href='#CmdDetach'>Detach</a> + <li><a href='#CmdTerminate'>Terminate</a> + <li><a href='#CmdSignal'>Signal</a> + <li><a href='#CmdStart'>Start</a> + </ul> + <li><a href='#Events'>Events</a> + <li><a href='#API'>API</a> +</ul> + +<h1>Processes Service</h1> + +<p>Processes service provides access to the target OS's process +information, allows to start and terminate a process, and allows +to attach and detach a process for debugging. Debug services, +like Memory and Run Control, require a process to be attached +before they can access it.</p> + +<p>Command and event parameters are encoded +as zero terminated <a href='TCF Specification.html#JSON'>JSON</a> strings.</p> + +<p>The service uses standard format for error reports, +see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p> + +<h2><a name='Cmds'>Commands</a></h2> + +<h3><a name='CmdGetContext'>Get Context</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Processes • getContext • <i><string: context ID></i> • +</font></b></pre> + +<p>The command retrieves context info for given context ID. +A context corresponds to an execution thread, process, address space, etc. +Context IDs are valid across TCF services, so it is allowed to issue 'Processes.getContext' +command with a context that was obtained, for example, from Memory service. +However, 'Processes.getContext' is supposed to return only process specific data. +If the ID is not a process ID, 'Processes.getContext' may not return any +useful information. +</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><context data></i> • + +<i><context data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p>Context data object should, at least, contain member +<b><font face="Courier New" size=2 color=#333399>"ID" : <i><string>.</i></font></b> +</p> + +<p>Predefined process context properties are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b></code> + - ID of the context, same as getContext command argument. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ParentID" : <i><string></i></font></b></code> + - parent context ID. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Name" : <i><string></i></font></b></code> + - process name. Client UI can show this name to a user. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Attached" : <i><boolean></i></font></b></code> + - true if the context is attached to debugger. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CanTerminate" : <i><boolean></i></font></b></code> + - true if the service can terminate the process. +</ul> + +<h3><a name='CmdGetChildren'>Get Children</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Processes • getChildren • <i><string: parent context ID></i> • +</font></b></pre> + +<p>The command requests a list of contexts available for process control commands.</p> + +<p>Parent context ID can be null – to retrieve top level of the hierarchy, can be one +of context IDs retrieved by previous getChildren commands, or it can be obtained from another service. +Contexts hierarchy can be simple plain list or it can form a tree. It is up to target agent developers to +choose layout that is most descriptive for a given target.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of context IDs></i> • + +<i><array of context IDs></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><context ID list></i> ] + +<i><context ID list></i> + <font face=Wingdings>Ø</font> <i><string: context ID></i> + <font face=Wingdings>Ø</font> <i><context ID list></i> , <i><string: context ID></i> +</font></b></pre> + +<h3><a name='CmdAttach'>Attach</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Processes • attach • <i><string: context ID></i> • +</font></b></pre> + +<p>The command attaches debugger to a process. +Services like Run Control, Memory, Breakpoints work only with attached processes.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdDetach'>Detach</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Processes • detach • <i><string: context ID></i> • +</font></b></pre> + +<p>The command detaches debugger from a process.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdTerminate'>Terminate</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Processes • terminate • <i><string: context ID></i> • +</font></b></pre> + +<p>The command terminates a process.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdSignal'>Signal</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Processes • signal • <i><string: context ID></i> • <i><int: signal></i> • +</font></b></pre> + +<p>The command sends a signal to a process.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdStart'>Start</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Processes • start • <i><string: working directory></i> • <i><string: program image file></i> • + <i><string array: command line></i> • <i><string array: environment variables></i> • <i><boolean: attach></i> • + +<i><string array></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><string list></i> ] + +<i><string list></i> + <font face=Wingdings>Ø</font> <i><string></i> + <font face=Wingdings>Ø</font> <i><string list></i> , <i><string></i> +</font></b></pre> + +<p>The command starts a new process on remote machine. +<i><string: working directory></i> - initial value of working directory for the process. +<i><string: program image file></i> - image file to start process with. +<i><string array: command line></i> - command line arguments for the process. +<i><string array: environment variables></i> - list of environment variables for the process, +they will be added to default process environment. +<i><boolean: attach></i> - if true debugger should be attached to the process.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><context data></i> • +</font></b></pre> + +<p>On success the command returns context data for created process. Context data has same format as Get Context result.</p> + +<h2><a name='Events'>Events</a></h2> + +<p>No events are currently defined for Processes service.</p> + +<h2><a name='API'>API</a></h2> + +<pre> +<font color=#7F0055>public interface</font> IProcesses <font color=#7F0055>extends</font> IService { + + <font color=#7F0055>static final</font> String NAME = "Processes"; + + <font color=#3F5FBF>/** + * Retrieve context info for given context ID. + * A context corresponds to an execution thread, process, address space, etc. + * Context IDs are valid across TCF services, so it is allowed to issue + * 'IProcesses.getContext' command with a context that was obtained, + * for example, from Memory service. + * However, 'Processes.getContext' is supposed to return only process specific data, + * If the ID is not a process ID, 'IProcesses.getContext' may not return any + * useful information + * + * <font color=#7F9FBF><font color=#7F9FBF>@param</font></font> id – context ID. + * <font color=#7F9FBF><font color=#7F9FBF>@param</font></font> done - call back interface called when operation is completed. + */</font> + IToken getContext(String id, DoneGetContext done); + + <font color=#3F5FBF>/** + * Client call back interface for getContext(). + */</font> + <font color=#7F0055>interface</font> DoneGetContext { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context – context data. + */</font> + <font color=#7F0055>void</font> doneGetContext(IToken token, Exception error, ProcessContext context); + } + + <font color=#3F5FBF>/** + * Retrieve children of given context. + * + * <font color=#7F9FBF>@param</font> parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + */</font> + IToken getChildren(String parent_context_id, <font color=#7F0055>boolean</font> attached_only, DoneGetChildren done); + + <font color=#3F5FBF>/** + * Client call back interface for getChildren(). + */</font> + <font color=#7F0055>interface</font> DoneGetChildren { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context_ids – array of available context IDs. + */</font> + <font color=#7F0055>void</font> doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + <font color=#3F5FBF>/** + * Context property names. + */</font> + <font color=#7F0055>static final</font> String + <font color=#3F5FBF>/** The TCF context ID */</font> + PROP_ID = "ID", + + <font color=#3F5FBF>/** The TCF parent context ID */</font> + PROP_PARENTID = "ParentID", + + <font color=#3F5FBF>/** Is the context attached */</font> + PROP_ATTACHED = "Attached", + + <font color=#3F5FBF>/** Can terminate the context */</font> + PROP_CAN_TERMINATE = "CanTerminate", + + <font color=#3F5FBF>/** Process name. Client UI can show this name to a user */</font> + PROP_NAME = "Name"; + + <font color=#7F0055>interface</font> ProcessContext { + + <font color=#3F5FBF>/** + * Get context ID. + * Same as getProperties().get(“ID”) + */</font> + String getID(); + + <font color=#3F5FBF>/** + * Get parent context ID. + * Same as getProperties().get(“ParentID”) + */</font> + String getParentID(); + + <font color=#3F5FBF>/** + * Get process name. + * Client UI can show this name to a user. + * Same as getProperties().get(“Name”) + */</font> + String getName(); + + <font color=#3F5FBF>/** + * Utility method to read context property PROP_ATTACHED. + * Services like IRunControl, IMemory, IBreakpoints work only with attached processes. + * <font color=#7F9FBF><font color=#7F9FBF>@return</font></font> value of PROP_ATTACHED. + */</font> + <font color=#7F0055>boolean</font> isAttached(); + + <font color=#3F5FBF>/** + * Utility method to read context property PROP_CAN_TERMINATE. + * <font color=#7F9FBF><font color=#7F9FBF>@return</font></font> value of PROP_CAN_TERMINATE. + */</font> + <font color=#7F0055>boolean</font> canTerminate(); + + <font color=#3F5FBF>/** + * Get all available context properties. + * <font color=#7F9FBF>@return</font> Map 'property name' -> 'property value' + */</font> + Map<String, Object> getProperties(); + + <font color=#3F5FBF>/** + * Attach debugger to a process. + * Services like IRunControl, IMemory, IBreakpoints work only with attached processes. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken attach(DoneCommand done); + + <font color=#3F5FBF>/** + * Detach debugger from a process. + * Process execution will continue without debugger supervision. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken detach(DoneCommand done); + + <font color=#3F5FBF>/** + * Terminate a process. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken terminate(DoneCommand done); + + <font color=#3F5FBF>/** + * Send a signal to a process. + * <font color=#7F9FBF>@param</font> signal - signal ID. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken signal(<font color=#7F0055>int</font> signal, DoneCommand done); + } + + <font color=#7F0055>interface</font> DoneCommand { + <font color=#7F0055>void</font> doneCommand(IToken token, Exception error); + } + + <font color=#3F5FBF>/** + * Start a new process on remote machine. + * <font color=#7F9FBF>@param</font> directory - initial value of working directory for the process. + * <font color=#7F9FBF>@param</font> file - process image file. + * <font color=#7F9FBF>@param</font> command_line - command line arguments for the process. + * <font color=#7F9FBF>@param</font> environment - list of environment variables for the process. + * <font color=#7F9FBF>@param</font> attach - if true debugger should be attached to the process. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken start(String directory, String file, + String[] command_line, String[] environment, <font color=#7F0055>boolean</font> attach, DoneStart done); + + <font color=#7F0055>interface</font> DoneStart { + <font color=#7F0055>void</font> doneStart(IToken token, Exception error, ProcessContext process); + } +} +</pre> + +</body> +</html> diff --git a/docs/TCF Service - Registers.html b/docs/TCF Service - Registers.html new file mode 100644 index 000000000..ef346af69 --- /dev/null +++ b/docs/TCF Service - Registers.html @@ -0,0 +1,506 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services - Registers</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services - Registers</h1> + +<ul> + <li><a href='#Cmds'>Commands</a> + <ul> + <li><a href='#CmdGetContext'>Get Context</a> + <li><a href='#CmdGetChildren'>Get Children</a> + <li><a href='#CmdSetRegister'>Set Register</a> + <li><a href='#CmdGetRegister'>Get Register</a> + </ul> + <li><a href='#Events'>Events</a> + <li><a href='#API'>API</a> +</ul> + +<h1>Registers Service</h1> + +<p>The service provides basic operations to read/write CPU and hardware registers. Command +and event parameters are encoded as zero terminated <a href='TCF Specification.html#JSON'>JSON</a> strings.</p> + +<p>The service uses standard format for error reports, +see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p> + +<h2><a name='Cmds'>Commands</a></h2> + +<h3><a name='CmdGetContext'>Get Context</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Registers • getContext • <i><string: context ID></i> • +</font></b></pre> + +<p>The command retrieves context info for given context ID. A context corresponds to an +register, register group, register bit field, etc. Exact meaning of a context depends on the target. +Target agent should define contexts hierarchy that is:</p> + +<ul type='disc'> + <li>Adequately reflects target hardware registers layout; + <li>Consistent with the lingo/terminology of the processor manuals; + <li>Intuitive to a user. +</ul> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><context data></i> • + +<i><context data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p>Context data object should, at least, contain member +<b><font face="Courier New" size=2 color=#333399>"ID" : <i><string>.</i></font></b> +Context data is expected to be cached by clients. +Service sends contextChanged event to notify changes in context data.</p> + +<p>Predefined register context properties are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b></code> + - ID of the context, same as getContext command argument. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ParentID" : <i><string></i></font></b></code> + - ID of a parent context. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ProcessID" : <i><string></i></font></b></code> + - process ID. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Name" : <i><string></i></font></b></code> + - context name. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Description" : <i><string></i></font></b></code> + - context description. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Formats" : <i><array of string></i></font></b></code> + - value formats available for register get/set commands. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Readable" : <i><boolean></i></font></b></code> + - true if context value can be read. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ReadOnce" : <i><boolean></i></font></b></code> + - true if reading the context (register) destroys its current value - it can be read only once. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Writeable" : <i><boolean></i></font></b></code> + - true if context value can be written. + + <li><code><b><font face="Courier New" size=2 color=#333399>"WriteOnce" : <i><boolean></i></font></b></code> + - true if register value can not be overwritten - every write counts. + + <li><code><b><font face="Courier New" size=2 color=#333399>"SideEffects" : <i><boolean></i></font></b></code> + - true if writing the context can change values of other registers. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Volatile" : <i><boolean></i></font></b></code> + - true if the register value can change even when target is stopped. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Float" : <i><boolean></i></font></b></code> + - true if the register value is a floating-point value. + + <li><code><b><font face="Courier New" size=2 color=#333399>"BigEndian" : <i><boolean></i></font></b></code> + - true if big endian, which means decreasing numeric significance with increasing bit number. + + <li><code><b><font face="Courier New" size=2 color=#333399>"LeftToRight" : <i><boolean></i></font></b></code> + - true if the lowest numbered bit (i.e. bit #0 or bit #1, depending on "FirstBit" value) should be shown to user as the left-most bit. + + <li><code><b><font face="Courier New" size=2 color=#333399>"FirstBit" : <i><int></i></font></b></code> + - 0 or 1. If the context has bit field children, bit positions of the fields can be zero-based or 1-based. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Bits" : <i><array of int></i></font></b></code> + - if context is a bit field, contains the field bit numbers in the parent context. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Values" : <i><array of named values></i></font></b></code> + - predefined names (mnemonics) for some of context values. + +</ul> + +<h3><a name='CmdGetChildren'>Get Children</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Registers • getChildren • <i><string: parent context ID></i> • +</font></b></pre> + +<p>The command requests a list of contexts available for registers access commands.</p> + +<p>Parent context ID is usually a thread ID retrieved through Run Control Service or one +of context IDs retrieved by previous getChildren commands. +Contexts hierarchy can be simple plain list of registers, or it can form a tree of register groups, registers and bit fields. +It is up to target agent developers to choose layout that is most descriptive for a given target.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of context IDs></i> •<i></i> + +<i><array of context IDs></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><context ID list></i> ] + +<i><context ID list></i> + <font face=Wingdings>Ø</font> <i><string: context ID></i> + <font face=Wingdings>Ø</font> <i><context ID list></i> , <i><string: context ID></i> + +</font></b></pre> + +<h3><a name='CmdSetRegister'>Set Register</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <token> • Registers • set • <i><string: context ID></i> • <i><string: value format></i> • <i><string: value></i> • +</font></b></pre> + +<p>Writes value into given register context. Context ID must be one returned by getContexts. +Value format must be one that is supported by the register context. +Client can get list of supported formats from context attributes.</p> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<p>Error report provides integer error code and a short, human readable explanation +of error.</p> + +<h3><a name='CmdGetRegister'>Get Register</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <token> • Registers • get • <i><string: context ID></i> • <i><string: value format></i> • +</font></b></pre> + +<p>Reads register value from given register context. Context ID must be one returned by getContexts. +Value format must be one that is supported by the register context. +Client can get list of supported formats from context attributes.</p> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><string: value></i> • +</font></b></pre> + +<p>Error report provides integer error code and a short, human readable explanation +of error. Value is formatted according to requested format.</p> + +<h2><a name='Events'>Events</a></h2> + +<p>Registers service broadcasts notification events when registers contexts are changed, and when +a register content is altered by "set" commands.</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +E • Registers • contextChanged • +E • Registers • registerChanged • <i><string: context ID></i> • +</font></b></pre> + +<h2><a name='API'>API</a></h2> + +<pre> +<font color=#3F5FBF>/** + * IRegisters service provides access to target CPU register values and properties. + */</font> +<font color=#7F0055>public interface</font> IRegisters <font color=#7F0055>extends</font> IService { + + <font color=#7F0055>static final</font> String NAME = "Registers"; + + <font color=#3F5FBF>/** + * Context property names. + */</font> + <font color=#7F0055>static final</font> String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_PROCESS_ID = "ProcessID", + PROP_NAME = "Name", + PROP_DESCRIPTION = "Description", + PROP_FORMATS = "Formats", + PROP_READBLE = "Readable", + PROP_READ_ONCE = "ReadOnce", + PROP_WRITEABLE = "Writeable", + PROP_WRITE_ONCE = "WriteOnce", + PROP_SIDE_EFFECTS = "SideEffects", + PROP_VOLATILE = "Volatile", + PROP_FLOAT = "Float", + PROP_BIG_ENDIAN = "BigEndian", + PROP_LEFT_TO_RIGHT = "LeftToRight", + PROP_FIST_BIT = "FirstBit", + PROP_BITS = "Bits", + PROP_VALUES = "Values"; + + <font color=#3F5FBF>/** + * Standard known formats for register data. + */</font> + <font color=#7F0055>static final</font> String + FORMAT_BINARY = "Binary", + FORMAT_OCTAL = "Octal", + FORMAT_DECIMAL = "Decimal", + FORMAT_HEX = "Hex", + FORMAT_NATURAL = "Natural"; + + <font color=#3F5FBF>/** + * Retrieve context info for given context ID. + * + * <font color=#7F9FBF>@param</font> id – context ID. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + */</font> + IToken getContext(String id, DoneGetContext done); + + <font color=#3F5FBF>/** + * Client call back interface for getContext(). + */</font> + <font color=#7F0055>interface</font> DoneGetContext { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context – context data. + */</font> + <font color=#7F0055>void</font> doneGetContext(IToken token, Exception error, RegistersContext context); + } + + <font color=#3F5FBF>/** + * Retrieve contexts available for registers commands. + * A context corresponds to an execution thread, stack frame, registers group, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + * + * <font color=#7F9FBF>@param</font> parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getChildren commands. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + */</font> + IToken getChildren(String parent_context_id, DoneGetChildren done); + + <font color=#3F5FBF>/** + * Client call back interface for getChildren(). + */</font> + <font color=#7F0055>interface</font> DoneGetChildren { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context_ids – array of available context IDs. + */</font> + <font color=#7F0055>void</font> doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + <font color=#3F5FBF>/** + * RegistersContext objects represent register groups, registers and bit fields. + */</font> + <font color=#7F0055>interface</font> RegistersContext { + <font color=#3F5FBF>/** + * Get Context ID. + * <font color=#7F9FBF>@return</font> context ID. + */</font> + String getID(); + + <font color=#3F5FBF>/** + * Get parent context ID. + * <font color=#7F9FBF>@return</font> parent context ID. + */</font> + String getParentID(); + + <font color=#3F5FBF>/** + * Get context (register, register group, bit field) name. + * <font color=#7F9FBF>@return</font> context name. + */</font> + String getName(); + + <font color=#3F5FBF>/** + * Get context description. + * <font color=#7F9FBF>@return</font> context description. + */</font> + String getDescription(); + + <font color=#3F5FBF>/** + * Get value formats available for register get/set commands. + * See FORMAT_* for knows format IDs definition. + * <font color=#7F9FBF>@return</font> array of supported format IDs. + */</font> + String[] getAvailableFormats(); + + <font color=#3F5FBF>/** + * Check if context value can be read. + * <font color=#7F9FBF>@return</font> true if can read value of the context. + */</font> + <font color=#7F0055>boolean</font> isReadable(); + + <font color=#3F5FBF>/** + * Check if reading the context (register) destroys its current value - + * it can be read only once. + * <font color=#7F9FBF>@return</font> true if read-once register. + */</font> + <font color=#7F0055>boolean</font> isReadOnce(); + + <font color=#3F5FBF>/** + * Check if context value can be written. + * <font color=#7F9FBF>@return</font> true if can write value of the context. + */</font> + <font color=#7F0055>boolean</font> isWriteable(); + + <font color=#3F5FBF>/** + * Check if register value can not be overwritten - every write counts. + * <font color=#7F9FBF>@return</font> true if write-once register. + */</font> + <font color=#7F0055>boolean</font> isWriteOnce(); + + <font color=#3F5FBF>/** + * Check if writing the context can change values of other registers. + * <font color=#7F9FBF>@return</font> true if has side effects. + */</font> + <font color=#7F0055>boolean</font> hasSideEffects(); + + <font color=#3F5FBF>/** + * Check if the register value can change even when target is stopped. + * <font color=#7F9FBF>@return</font> true if the register value can change at any time. + */</font> + <font color=#7F0055>boolean</font> isVolatile(); + + <font color=#3F5FBF>/** + * Check if the register value is a floating-point value. + * <font color=#7F9FBF>@return</font> true if a floating-point register. + */</font> + <font color=#7F0055>boolean</font> isFloat(); + + <font color=#3F5FBF>/** + * Check endianess of the context. + * Big endian means decreasing numeric significance with increasing bit number. + * <font color=#7F9FBF>@return</font> true if big endian. + */</font> + <font color=#7F0055>boolean</font> isBigEndian(); + + <font color=#3F5FBF>/** + * Check if the lowest numbered bit (i.e. bit #0 or bit #1 depending on + * getFirstBitNumber() value) should be shown to user as the left-most bit or + * the right-most bit. + * <font color=#7F9FBF>@return</font> true if the first bit is left-most bit. + */</font> + <font color=#7F0055>boolean</font> isLeftToRight(); + + <font color=#3F5FBF>/** + * If the context has bit field children, bit positions of the fields + * can be zero-based or 1-based. + * <font color=#7F9FBF>@return</font> first bit position - 0 or 1. + */</font> + <font color=#7F0055>int</font> getFirstBitNumber(); + + <font color=#3F5FBF>/** + * If context is a bit field, get the field bit numbers in parent context. + * <font color=#7F9FBF>@return</font> array of bit numbers. + */</font> + <font color=#7F0055>int</font>[] getBitNumbers(); + + <font color=#3F5FBF>/** + * A context can have predefined names (mnemonics) for some its values. + * This method returns a list of such named values. + * <font color=#7F9FBF>@return</font> array of named values or null. + */</font> + NamedValue[] getNamedValues(); + + <font color=#3F5FBF>/** + * Get complete map of context properties. + * <font color=#7F9FBF>@return</font> map of context properties. + */</font> + Map<String,Object> getProperties(); + + <font color=#3F5FBF>/** + * Read value of the context. + * <font color=#7F9FBF>@param</font> format - ID of a format to use for result value. + * <font color=#7F9FBF>@param</font> done - call back object. + * <font color=#7F9FBF>@return</font> - pending command handle. + */</font> + IToken get(String format, DoneGet done); + + <font color=#3F5FBF>/** + * Set value of the context. + * <font color=#7F9FBF>@param</font> format - ID of a format used for value. + * <font color=#7F9FBF>@param</font> value - value to write into the context. + * <font color=#7F9FBF>@param</font> done - call back object. + * <font color=#7F9FBF>@return</font> - pending command handle. + */</font> + IToken set(String format, String value, DoneSet done); + } + + <font color=#3F5FBF>/** + * A register context can have predefined names (mnemonics) for some its values. + * NamedValue objects represent such values. + */</font> + <font color=#7F0055>interface</font> NamedValue { + <font color=#3F5FBF>/** + * Get number associated with this named value. + * <font color=#7F9FBF>@return</font> the value as a number. + */</font> + Number getValue(); + + <font color=#3F5FBF>/** + * Get name (mnemonic) of the value. + * <font color=#7F9FBF>@return</font> value name. + */</font> + String getName(); + + <font color=#3F5FBF>/** + * Get human readable description of the value. + * <font color=#7F9FBF>@return</font> value description. + */</font> + String getDescription(); + } + + <font color=#3F5FBF>/** + * 'get' command call back interface. + */</font> + <font color=#7F0055>interface</font> DoneGet { + <font color=#7F0055>void</font> doneGet(IToken token, Exception error, String value); + } + + <font color=#3F5FBF>/** + * 'set' command call back interface. + */</font> + <font color=#7F0055>interface</font> DoneSet { + <font color=#7F0055>void</font> doneSet(IToken token, Exception error); + } + + <font color=#3F5FBF>/** + * Add registers service event listener. + * <font color=#7F9FBF>@param</font> listener - event listener implementation. + */</font> + <font color=#7F0055>void</font> addListener(RegistersListener listener); + + <font color=#3F5FBF>/** + * Remove registers service event listener. + * <font color=#7F9FBF>@param</font> listener - event listener implementation. + */</font> + <font color=#7F0055>void</font> removeListener(RegistersListener listener); + + <font color=#3F5FBF>/** + * Registers event listener is notified when registers context hierarchy + * changes, and when a register is modified by the service commands. + */</font> + <font color=#7F0055>interface</font> RegistersListener { + + <font color=#3F5FBF>/** + * Called when register context properties changed. + * Most targets have static set of registers and register properties. + * Such targets never generate this event. However, some targets, + * for example, JTAG probes, allow user to modify register definitions. + * Clients should flush all cached register context data. + */</font> + <font color=#7F0055>void</font> contextChanged(); + + <font color=#3F5FBF>/** + * Called when register content was changed and clients + * need to update themselves. Clients, at least, should invalidate + * corresponding cached registers data. + * Not every change is notified - it is not possible, + * only those, which are not caused by normal execution of the debuggee. + */</font> + <font color=#7F0055>void</font> registerChanged(String context_id); + } +} +</pre> + +</body> +</html> diff --git a/docs/TCF Service - Run Control.html b/docs/TCF Service - Run Control.html new file mode 100644 index 000000000..dc17b079b --- /dev/null +++ b/docs/TCF Service - Run Control.html @@ -0,0 +1,618 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services - Run Control</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services - Run Control</h1> + +<ul> + <li><a href='#Cmds'>Commands</a> + <ul> + <li><a href='#CmdGetContext'>Get Context</a> + <li><a href='#CmdGetChildren'>Get Children</a> + <li><a href='#CmdSuspend'>Suspend</a> + <li><a href='#CmdResume'>Resume</a> + <li><a href='#CmdGetState'>Get State</a> + <li><a href='#CmdTerminate'>Terminate</a> + </ul> + <li><a href='#Events'>Events</a> + <li><a href='#API'>API</a> +</ul> + +<h1>Run Control Service</h1> + +<p>The service provides basic run control operations for execution contexts on a target. +Command and event parameters are encoded as zero terminated <a href='TCF Specification.html#JSON'>JSON</a> strings.</p> + +<p>The service uses standard format for error reports, +see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p> + +<h2><a name='Cmds'>Commands</a></h2> + +<p>All run control commands are fully asynchronous, which means they never wait until +context is in a particular state. For example, if single step command arrives when +context is running, it does not wait until it stops, but returns an error. If a command +successfully resumed a context, it does not wait until instruction pointer reaches +desired destination – from client point of view the command execution ends right after +context was resumed. Various stepping commands can leave a context running in a special +mode, which is different from normal execution, for example, it can leave temporary +breakpoints to suspend the context when control reaches a particular place. Such execution +mode ends when the context is suspended, even if it was suspended for reasons unrelated +to the command and intended destination was not reached. Client can know when and +why a context is suspended by listening to events.</p> + +<h3><a name='CmdGetContext'>Get Context</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • RunControl • getContext • <i><string: context ID></i> • +</font></b></pre> + +<p>The command retrieves context properties for given context ID. +Exact meaning of context depends on the target. +A context can represent an execution thread, a process, an address space, etc. +A context can belong to a parent context. Contexts hierarchy can be simple +plain list or it can form a tree. It is up to target agent developers to choose +layout that is most descriptive for a given target. Context IDs are valid across +all services. In other words, all services access same hierarchy of contexts, +with same IDs, however, each service accesses its own subset of context's +attributes and functionality, which is relevant to that service.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><context data></i> • + +<i><context data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><object: context properties></i> +</font></b></pre> + +<p>Context data object is collection of context properties. It should, at least, contain member +<b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b>. +It can also contain arbitrary number of components +describing context properties and capabilities. Context data is supposed to be cached +by clients and it is not expected to change frequently. It can include, for example, +context name or ability to perform single step command on the context. But, it should +not include volatile data like current PC or running/suspended state. Service sends +contextChanged event to notify changes in context data.</p> + +<p>Predefined run control context properties are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b></code> + - ID of the context, same as getContext command argument. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ParentID" : <i><string></i></font></b></code> + - ID of a parent context. + + <li><code><b><font face="Courier New" size=2 color=#333399>"IsContainer" : <i><boolean></i></font></b></code> + - true if the context is a container. + Executing resume or suspend command on a container causes all its children to resume or suspend. + + <li><code><b><font face="Courier New" size=2 color=#333399>"HasState" : <i><boolean></i></font></b></code> + - true if the context is an execution context, therefore + has an execution state, like state of a program counter (PC). + Only context that has a state can be resumed or suspended. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CanSuspend" : <i><boolean></i></font></b></code> + - true if Suspend command is supported for this contex. It does not mean that the command can be executed successfully in + the current state of the context. For example, the command still can fail if context is already suspended. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CanResume" : <i><int: bitset of resume modes></i></font></b></code> + - for each resume mode, corresponding bit is '1' if Resume command mode is supported for this contex, and '0' otherwise. + It does not mean that the command can be executed successfully in + the current state of the context. For example, the command still can fail if context is already resumed. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CanCount" : <i><int: bitset of resume modes></i></font></b></code> + - for each resume mode, corresponding bit is '1' if Resume command mode with count other then 1 is supported by the context. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CanTerminate" : <i><boolean></i></font></b></code> + - true if Terminate command is supported by the context, +</ul> + +<h3><a name='CmdGetChildren'>Get Children</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • RunControl • getChildren • <i><string: parent context ID></i> • +</font></b></pre> + +<p>The command requests list of execution contexts available for run control commands.</p> + +<p>Parent context ID can be null – to retrieve top level of the hierarchy, can be one +of context IDs retrieved by previous getChildren commands, or it can be obtained from another service. +Contexts hierarchy can be simple plain list or it can form a tree. It is up to target agent developers to +choose layout that is most descriptive for a given target.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of context IDs></i> • + +<i><array of context IDs></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ <i><context ID list></i> ] + +<i><context ID list></i> + <font face=Wingdings>Ø</font> <i><string: context ID></i> + <font face=Wingdings>Ø</font> <i><context ID list></i> , <i><string: context ID></i> +</font></b></pre> + +<h3><a name='CmdSuspend'>Suspend</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • RunControl • suspend • <i><string: context ID></i> • +</font></b></pre> + +<p>The command suspends execution of given context. The command should fail if CanSuspend property of the context is false. +If context's IsContainer = true, the command is propagated to context's children. Only contexts with HasState = true +can be suspended.</p> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdResume'>Resume</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • RunControl • resume • <i><string: context ID></i> • <i><int: mode></i> • <i><int: count></i> • +</font></b></pre> + +<p>The command resumes execution of given context. The command should fail if CanResume +property of the context is '0' for given mode. If context's IsContainer = true, the command is propagated +to context's children. Only contexts with HasState = true can be resumed.</p> + +<p>Resume modes:</p> +<ul> + <li><code>RM_RESUME = 0</code> - rusume normal execution. Execution will + continue until suspended by command or breakpoint. + + <li><code>RM_STEP_OVER = 1</code> - step over single instruction. If instruction + is function call, execution continues until control returns from the function. + + <li><code>RM_STEP_INTO = 2</code> - single instruction in given context. + + <li><code>RM_STEP_OVER_LINE = 3</code> - resume execution of given context until control reaches instruction + that belongs to a different line of source code, but runs any functions called at + full speed. Error is returned if line number information not available. + + <li><code>RM_STEP_INTO_LINE = 4</code> - resumes execution of given context until control reaches instruction + that belongs to a different line of source code. If a function is called, + stop at first line of the function code. Error is returned if line number + information not available. + + <li><code>RM_STEP_OUT = 5</code> - resume execution of given context until control returns from current + function. +</ul> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='CmdGetState'>Get State</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • RunControl • getState • <i><string: context ID></i> • +</font></b></pre> + +<p>The command retrieves current state of the context. The command should fail if HasState property of +the context is false.</p> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><boolean: suspended></i> • + <i><int: PC></i> • <i><string: last state change reason></i> • <i><state data></i> • + +<i><state data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><object: context state properties></i> +</font></b></pre> + +<p>State change reason can be any text, but if it is one of predefined strings, +a generic client might be able to handle it better. Predefined reasons are:</p> +<ul> + <li><code>REASON_USER_REQUEST = "Suspended"</code> - context suspended by command. + <li><code>REASON_STEP = "Step"</code> - context resumed or suspended by step command. + <li><code>REASON_BREAKPOINT = "Breakpoint"</code> - context suspended by breakpoint. + <li><code>REASON_EXCEPTION = "Exception"</code> - context suspended by exception. + <li><code>REASON_CONTAINER = "Container"</code> - context suspended or resumed as part of container. + <li><code>REASON_WATCHPOINT = "Watchpoint"</code> - context suspended by watchpoint (data breakpoint). + <li><code>REASON_SIGNAL = "Signal"</code> - context suspended because it received a signal. + <li><code>REASON_SHAREDLIB = "Shared Library"</code> - context suspended because a shared library is loaded or unloaded. + <li><code>REASON_ERROR = "Error"</code> - context suspended because of an error in execution environment. +</ul> + +<p>Context state properties can contain any data relevant to context state. +Defenition of state properties depends on a target.</p> + +<h3><a name='CmdTerminate'>Terminate</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • RunControl • terminate • <i><string: context ID></i> • +</font></b></pre> + +<p>The command terminates execution of given context. The command should fail if CanTerminate +property of the context is false.</p> + +<p>Result message:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h2><a name='Events'>Events</a></h2> + +<pre><b><font face="Courier New" size=2 color=#333399> +E • RunControl • contextAdded • <i><array of context data></i> • + +E • RunControl • contextChanged • <i><array of context data></i> • + +E • RunControl • contextRemoved • <i><array of context IDs></i> • + +E • RunControl • contextSuspended • <i><string: context ID></i> • <i><int: PC></i> • + <i><string: reason></i> • <i><state data></i> • + +E • RunControl • contextResumed • <i><string: context ID></i> • + +E • RunControl • contextException • <i><string: context ID></i> • <i><string: description></i> • + +E • RunControl • containerSuspended • <i><string: context ID></i> • <i><int: PC></i> • + <i><string: reason></i> • <i><state data></i> • <i><array of context IDs></i> • + +E • RunControl • containerResumed • <i><array of context IDs></i> • + +<i><array of context data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ <i><context data list></i> ] + +<i><context data list></i> + <font face=Wingdings>Ø</font> <i><object: context data></i> + <font face=Wingdings>Ø</font> <i><context data list></i> , <i><object: context data></i> +</font></b></pre> + +<dl> + <dt><b>contextAdded</b> + <dd>is sent when new contexts are created or attached for debugging. The message contains + array of context data. Context data is same as returned by Get Context command. + <dt><b>contextChanged</b> + <dd>is sent when context properties change. The message contains + array of changed (new) context data. Context data is same as returned by Get Context command. + <dt><b>contextRemoved</b> + <dd>is sent when context is removed - terminated or dettached. The message contains + array of context IDs. + <dt><b>contextSuspended</b> + <dd>is sent when context is suspended. The message context ID contains context state data, + same state data as returned by Get State command. + <dt><b>contextResumed</b> + <dd>is sent when context is resumed. The message contains resumed context ID. + <dt><b>contextException</b> + <dd>is sent when execution exception occurs in a context. The message contains context ID and + a string that describes nature of the exception. + <dt><b>containerSuspended</b> + <dd>is sent when target simultaneously suspends multiple threads in a container (process, core, etc.). + The message contains context ID and context state data of a context responsible for the event. + It can be container ID or any one of container children, for example, it can be thread + that hit "suspend all" breakpoint. Message also contains full list of all contexts that were suspended + simultaneously. No separate contextSuspened events are sent for contexts in the list. If client needs + state data for those contexts, it should use Get State command. + <dt><b>containerResumed</b> + <dd>is sent when target simultaneously resumes multiple threads in a container (process, + core, etc.). Message contains full list of all contexts that were resumed + simultaneously. No separate contextResumed events are sent for contexts in the list. +</dl> + +<h2><a name='API'>API</a></h2> + +<pre> +<font color=#7F0055>public interface</font> IRunControl <font color=#7F0055>extends</font> IService { + + <font color=#3F5FBF>/** + * Context property names. + */</font> + <font color=#7F0055>static final</font> String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_IS_CONTAINER = "IsContainer", + PROP_HAS_STATE = "HasState", + PROP_CAN_RESUME = "CanResume", + PROP_CAN_COUNT = "CanCount", + PROP_CAN_SUSPEND = "CanSuspend", + PROP_CAN_TERMINATE = "CanTerminate"; + + <font color=#3F5FBF>/** + * Context resume modes. + */</font> + <font color=#7F0055>static final int</font> + RM_RESUME = 0, + RM_STEP_OVER = 1, + RM_STEP_INTO = 2, + RM_STEP_OVER_LINE = 3, + RM_STEP_INTO_LINE = 4, + RM_STEP_OUT = 5; + + <font color=#3F5FBF>/** + * State change reason of a context. + * Reason can be any text, but if it is one of predefined strings, + * a generic client might be able to handle it better. + */</font> + <font color=#7F0055>static final</font> String + REASON_USER_REQUEST = "Suspended", + REASON_STEP = "Step", + REASON_BREAKPOINT = "Breakpoint", + REASON_EXCEPTION = "Exception", + REASON_CONTAINER = "Container", + REASON_WATCHPOINT = "Watchpoint", + REASON_SIGNAL = "Signal", + REASON_SHAREDLIB = "Shared Library", + REASON_ERROR = "Error"; + + <font color=#3F5FBF>/** + * Retrieve context info for given context ID. + * + * <font color=#7F9FBF>@param</font> id – context ID. + * <font color=#7F9FBF>@param</font> done - callback interface called when operation is completed. + */</font> + IToken getContext(String id, DoneGetContext done); + + <font color=#3F5FBF>/** + * Client callback interface for getContext(). + */</font> + <font color=#7F0055>interface</font> DoneGetContext { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context – context data. + */</font> + <font color=#7F0055>void</font> doneGetContext(IToken token, Exception error, RunControlContext context); + } + + <font color=#3F5FBF>/** + * Retrieve children of given context. + * + * <font color=#7F9FBF>@param</font> parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * <font color=#7F9FBF>@param</font> done - callback interface called when operation is completed. + */</font> + IToken getChildren(String parent_context_id, DoneGetChildren done); + + <font color=#3F5FBF>/** + * Client callback interface for getContexts(). + */</font> + <font color=#7F0055>interface</font> DoneGetChildren { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> contexts – array of available context IDs. + */</font> + <font color=#7F0055>void</font> doneGetChildren(IToken token, RunControlError error, Context[] contexts); + } + + <font color=#3F5FBF>/** + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + */</font> + <font color=#7F0055>interface</font> RunControlContext { + + <font color=#3F5FBF>/** + * Retrieve context ID. + * Same as getProperties().get("ID") + */</font> + String getID(); + + <font color=#3F5FBF>/** + * Retrieve parent context ID. + * Same as getProperties().get("ParentID") + */</font> + String getParentID(); + + <font color=#3F5FBF>/** + * Get context properties. See PROP_* definitions for property names. + * Context properties are read only, clients should not try to modify them. + * <font color=#7F9FBF>@return</font> Map of context properties. + */</font> + Map<String,Object> getProperties(); + + <font color=#3F5FBF>/** + * Utility method to read context property PROP_IS_CONTAINER. + * Executing resume or suspend command on a container causes all its children to resume or suspend. + * <font color=#7F9FBF>@return</font> value of PROP_IS_CONTAINER. + */</font> + <font color=#7F0055>boolean</font> isContainer(); + + <font color=#3F5FBF>/** + * Utility method to read context property PROP_HAS_STATE. + * Only context that has a state can be resumed or suspended. + * <font color=#7F9FBF>@return</font> value of PROP_HAS_STATE. + */</font> + <font color=#7F0055>boolean</font> hasState(); + + <font color=#3F5FBF>/** + * Utility method to read context property PROP_CAN_SUSPEND. + * Value 'true' means suspend command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already suspended. + * <font color=#7F9FBF>@return</font> value of PROP_CAN_SUSPEND. + */</font> + <font color=#7F0055>boolean</font> canSuspend(); + + <font color=#3F5FBF>/** + * Utility method to read a 'mode' bit in context property PROP_CAN_RESUME. + * Value 'true' means resume command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already resumed. + * <font color=#7F9FBF>@param</font> mode - resume mode, see RM_*. + * <font color=#7F9FBF>@return</font> value of requested bit of PROP_CAN_RESUME. + */</font> + <font color=#7F0055>boolean</font> canResume(<font color=#7F0055>int</font> mode); + + <font color=#3F5FBF>/** + * Utility method to read a 'mode' bit in context property PROP_CAN_COUNT. + * Value 'true' means resume command with count other then 1 is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already resumed. + * <font color=#7F9FBF>@param</font> mode - resume mode, see RM_*. + * <font color=#7F9FBF>@return</font> value of requested bit of PROP_CAN_COUNT. + */</font> + <font color=#7F0055>boolean</font> canCount(<font color=#7F0055>int</font> mode); + + <font color=#3F5FBF>/** + * Utility method to read context property PROP_CAN_TERMINATE. + * Value 'true' means terminate command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already exited. + * <font color=#7F9FBF>@return</font> value of PROP_CAN_SUSPEND. + */</font> + <font color=#7F0055>boolean</font> canTerminate(); + + <font color=#3F5FBF>/** + * Send a command to retrieve current state of a context. + * <font color=#7F9FBF>@param</font> done - command result call back object. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken getState(DoneGetState done); + + <font color=#3F5FBF>/** + * Send a command to suspend a context. + * Also suspends children if context is a container. + * <font color=#7F9FBF>@param</font> done - command result call back object. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken suspend(DoneCommand done); + + <font color=#3F5FBF>/** + * Send a command to resume a context. + * Also resumes children if context is a container. + * <font color=#7F9FBF>@param</font> mode - defines how to resume the context, see RM_*. + * <font color=#7F9FBF>@param</font> count - if mode implies stepping, defines how many steps to perform. + * <font color=#7F9FBF>@param</font> done - command result call back object. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken resume(<font color=#7F0055>int</font> mode, <font color=#7F0055>int</font> count, DoneCommand done); + + <font color=#3F5FBF>/** + * Send a command to terminate a context. + * <font color=#7F9FBF>@param</font> done - command result call back object. + * <font color=#7F9FBF>@return</font> pending command handle, can be used to cancel the command. + */</font> + IToken terminate(DoneCommand done); + } + + <font color=#7F0055>class</font> RunControlError <font color=#7F0055>extends</font> Exception { + } + + <font color=#7F0055>interface</font> DoneGetState { + <font color=#7F0055>void</font> doneGetState(IToken token, Exception error, <font color=#7F0055>boolean</font> suspended, String pc, + String reason, Map<String,Object> params); + } + + <font color=#7F0055>interface</font> DoneCommand { + <font color=#3F5FBF>/** + * Called when run control command execution is complete. + * <font color=#7F9FBF>@param</font> token - pending command handle. + * <font color=#7F9FBF>@param</font> error - command execution error or null. + */</font> + <font color=#7F0055>void</font> doneCommand(IToken token, Exception error); + } + + <font color=#3F5FBF>/** + * Add run control event listener. + * <font color=#7F9FBF>@param</font> listener - run control event listener to add. + */</font> + <font color=#7F0055>void</font> addListener(RunControlListener listener); + + <font color=#3F5FBF>/** + * Remove run control event listener. + * <font color=#7F9FBF>@param</font> listener - run control event listener to remove. + */</font> + <font color=#7F0055>void</font> removeListener(RunControlListener listener); + + <font color=#3F5FBF>/** + * Service events listener interface. + */</font> + <font color=#7F0055>interface</font> RunControlListener { + + <font color=#3F5FBF>/** + * Called when a new contexts are created. + * <font color=#7F9FBF>@param</font> contexts - array of new context properties. + */</font> + <font color=#7F0055>void</font> contextAdded(RunControlContext contexts[]); + + <font color=#3F5FBF>/** + * Called when a context properties changed. + * <font color=#7F9FBF>@param</font> contexts - array of new context properties. + */</font> + <font color=#7F0055>void</font> contextChanged(RunControlContext contexts[]); + + <font color=#3F5FBF>/** + * Called when contexts are removed. + * <font color=#7F9FBF>@param</font> context_ids - aray of removed context IDs. + */</font> + <font color=#7F0055>void</font> contextRemoved(String context_ids[]); + + <font color=#3F5FBF>/** + * Called when a thread is suspended. + * <font color=#7F9FBF>@param</font> context - ID of a context that was suspended. + * <font color=#7F9FBF>@param</font> pc - program counter of the context, can be null. + * <font color=#7F9FBF>@param</font> reason - human readable description of suspend reason. + * <font color=#7F9FBF>@param</font> params - additional, target specific data about suspended context. + */</font> + <font color=#7F0055>void</font> contextSuspended(String context, String pc, + String reason, Map<String,Object> params); + + <font color=#3F5FBF>/** + * Called when a thread is resumed. + * <font color=#7F9FBF>@param</font> context - ID of a context that was resumed. + */</font> + <font color=#7F0055>void</font> contextResumed(String context); + + <font color=#3F5FBF>/** + * Called when target simultaneously suspends multiple threads in a container + * (process, core, etc.). + * + * <font color=#7F9FBF>@param</font> context - ID of a context responsible for the event. It can be container ID or + * any one of container children, for example, it can be thread that hit "suspend all" breakpoint. + * Client expected to move focus (selection) to this context. + * <font color=#7F9FBF>@param</font> pc - program counter of the context. + * <font color=#7F9FBF>@param</font> reason - human readable description of suspend reason. + * <font color=#7F9FBF>@param</font> params - additional target specific data about suspended context. + * <font color=#7F9FBF>@param</font> suspended_ids - full list of all contexts that were suspended. + */</font> + <font color=#7F0055>void</font> containerSuspended(String context, String pc, + String reason, Map<String,Object> params, String[] suspended_ids); + + <font color=#3F5FBF>/** + * Called when target simultaneously resumes multiple threads in a container (process, + * core, etc.). + * + * <font color=#7F9FBF>@param</font> context_ids - full list of all contexts that were resumed. + */</font> + <font color=#7F0055>void</font> containerResumed(String[] context_ids); + + <font color=#3F5FBF>/** + * Called when an exception is detected in a target thread. + * <font color=#7F9FBF>@param</font> context - ID of a context that caused an exception. + * <font color=#7F9FBF>@param</font> msg - human readable description of the exception. + */</font> + <font color=#7F0055>void</font> contextException(String context, String msg); + } +} +</pre> + +</body> +</html> diff --git a/docs/TCF Service - Stack Trace.html b/docs/TCF Service - Stack Trace.html new file mode 100644 index 000000000..c1ce675d5 --- /dev/null +++ b/docs/TCF Service - Stack Trace.html @@ -0,0 +1,278 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services - Stack Trace</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services - Stack Trace</h1> + +<ul> + <li><a href='#Cmds'>Commands</a> + <ul> + <li><a href='#CmdGetContext'>Get Context</a> + <li><a href='#CmdGetChildren'>Get Children</a> + </ul> + <li><a href='#Events'>Events</a> + <li><a href='#API'>API</a> +</ul> + +<h1>Stack Trace Service</h1> + +<p>The service implements thread stack back tracing. Command +and event parameters are encoded as zero terminated <a href='TCF Specification.html#JSON'>JSON</a> strings.</p> + +<p>The service uses standard format for error reports, +see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p> + +<h2><a name='Cmds'>Commands</a></h2> + +<h3><a name='CmdGetContext'>Get Context</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • StackTrace • getContext • <i><array of context IDs></i> • + +<i><array of context IDs></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><context ID list></i> ] + +<i><context ID list></i> + <font face=Wingdings>Ø</font> <i><string: context ID></i> + <font face=Wingdings>Ø</font> <i><context ID list></i> , <i><string: context ID></i> +</font></b></pre> + +<p>The command retrieves context info for given context IDs. +Command allows to query multiple contexts at once. +Stack Trace context represents single stack frame. +If target supports more then one stack per thread, +each stack is also represented by a separate context.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><array of context data></i> • <i><error report></i> • + +<i><array of context data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><context data list></i> ] + +<i><context data list></i> + <font face=Wingdings>Ø</font> <i><context data></i> + <font face=Wingdings>Ø</font> <i><context data list></i> , <i><context data></i> + +<i><context data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p>Context data object should, at least, contain member +<b><font face="Courier New" size=2 color=#333399>"ID" : <i><string>.</i></font></b> +Context data is expected to be cached by clients. +Cached context data should by flushed when parent thread is resumed.</p> + +<p>Predefined stack trace context properties are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b></code> + - ID of the context, same as getContext command argument. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ParentID" : <i><string></i></font></b></code> + - ID of a parent context. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ProcessID" : <i><string></i></font></b></code> + - process ID. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Name" : <i><string></i></font></b></code> + - context name if context is a stack + + <li><code><b><font face="Courier New" size=2 color=#333399>"FP" : <i><number></i></font></b></code> + - frame pointer - memory address of stack frame + + <li><code><b><font face="Courier New" size=2 color=#333399>"PC" : <i><number></i></font></b></code> + - program counter - memory address of instruction that will be executed when thread returns from this stack frame. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ArgsCnt" : <i><number></i></font></b></code> + - function arguments count + + <li><code><b><font face="Courier New" size=2 color=#333399>"ArgsAddr" : <i><number></i></font></b></code> + - memory address of function arguments + + <li><code><b><font face="Courier New" size=2 color=#333399>"Level" : <i><number></i></font></b></code> + - frame level. Bottom most (oldest) frame is level 0. +</ul> + +<h3><a name='CmdGetChildren'>Get Children</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • StackTrace • getChildren • <i><string: parent context ID></i> • +</font></b></pre> + +<p>The command retrieves stack trace context list. +Parent context usually corresponds to an execution thread. +Some targets have more then one stack. In such case children of a thread +are stacks, and stack frames are deeper in the hierarchy - they can be +retrieved with additional getChildren commands.</p> + +<p>The command will fail if parent thread is not suspended. +Client can use Run Control service to suspend a thread.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of context IDs></i> •<i></i> + +<i><array of context IDs></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><context ID list></i> ] + +<i><context ID list></i> + <font face=Wingdings>Ø</font> <i><string: context ID></i> + <font face=Wingdings>Ø</font> <i><context ID list></i> , <i><string: context ID></i> + +</font></b></pre> + + +<h2><a name='Events'>Events</a></h2> + +<p>No events are currently defined for Stack Trace service.</p> + +<h2><a name='API'>API</a></h2> + +<pre> +<font color=#7F0055>public interface</font> IStackTrace <font color=#7F0055>extends</font> IService { + + <font color=#7F0055>static final</font> String NAME = "StackTrace"; + + <font color=#3F5FBF>/** + * Context property names. + */</font> + <font color=#7F0055>static final</font> String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_PROCESS_ID = "ProcessID", + PROP_NAME = "Name", + PROP_FRAME_ADDRESS = "FP", + PROP_PROGRAM_COUNTER = "PC", + PROP_ARGUMENTS_COUNT = "ArgsCnt", + PROP_ARGUMENTS_ADDRESS = "ArgsAddr", + PROP_LEVEL = "Level"; + + <font color=#3F5FBF>/** + * Retrieve context info for given context IDs. + * + * The command will fail if parent thread is not suspended. + * Client can use Run Control service to suspend a thread. + * + * <font color=#7F9FBF>@param</font> id – array of context IDs. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + */</font> + IToken getContext(String[] id, DoneGetContext done); + + <font color=#3F5FBF>/** + * Client call back interface for getContext(). + */</font> + <font color=#7F0055>interface</font> DoneGetContext { + <font color=#3F5FBF>/** + * Called when context data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context – array of context data or null if error. + */</font> + <font color=#7F0055>void</font> doneGetContext(IToken token, Exception error, StackTraceContext[] context); + } + + <font color=#3F5FBF>/** + * Retrieve stack trace context list. + * Parent context usually corresponds to an execution thread. + * Some targets have more then one stack. In such case children of a thread + * are stacks, and stack frames are deeper in the hierarchy - they can be + * retrieved with additional getChildren commands. + * + * The command will fail if parent thread is not suspended. + * Client can use Run Control service to suspend a thread. + * + * <font color=#7F9FBF>@param</font> parent_context_id – parent context ID. + * <font color=#7F9FBF>@param</font> done - call back interface called when operation is completed. + */</font> + IToken getChildren(String parent_context_id, DoneGetChildren done); + + <font color=#3F5FBF>/** + * Client call back interface for getChildren(). + */</font> + <font color=#7F0055>interface</font> DoneGetChildren { + <font color=#3F5FBF>/** + * Called when context list retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context_ids – array of available context IDs. + * Stack frames are ordered from stack bottom to top. + */</font> + <font color=#7F0055>void</font> doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + <font color=#3F5FBF>/** + * StackTraceContext represents stack trace objects - stacks and stack frames. + */</font> + <font color=#7F0055>interface</font> StackTraceContext { + + <font color=#3F5FBF>/** + * Get Context ID. + * <font color=#7F9FBF>@return</font> context ID. + */</font> + String getID(); + + <font color=#3F5FBF>/** + * Get parent context ID. + * <font color=#7F9FBF>@return</font> parent context ID. + */</font> + String getParentID(); + + <font color=#3F5FBF>/** + * Get context name - if context represents a stack. + * <font color=#7F9FBF>@return</font> context name or null. + */</font> + String getName(); + + <font color=#3F5FBF>/** + * Get memory address of this frame. + * <font color=#7F9FBF>@return</font> address or null if not a stack frame. + */</font> + Number getFrameAddress(); + + <font color=#3F5FBF>/** + * Get program counter saved in this stack frame - + * it is address of instruction to be executed when the function returns. + * <font color=#7F9FBF>@return</font> program counter or null if not a stack frame. + */</font> + Number getProgramCounter(); + + <font color=#3F5FBF>/** + * Get number of function arguments for this frame. + * <font color=#7F9FBF>@return</font> function arguments count. + */</font> + <font color=#7F0055>int</font> getArgumentsCount(); + + <font color=#3F5FBF>/** + * Get address of function arguments area in memory. + * <font color=#7F9FBF>@return</font> function arguments address or null if not available. + */</font> + Number getArgumentsAddress(); + + <font color=#3F5FBF>/** + * Get stack frame level. + * <font color=#7F9FBF>@return</font> frame level or 0 if not a stack frame. + */</font> + <font color=#7F0055>int</font> getLevel(); + + <font color=#3F5FBF>/** + * Get complete map of context properties. + * <font color=#7F9FBF>@return</font> map of context properties. + */</font> + Map<String,Object> getProperties(); + } +} +</pre> + +</body> +</html> diff --git a/docs/TCF Service - System Monitor.html b/docs/TCF Service - System Monitor.html new file mode 100644 index 000000000..882e91094 --- /dev/null +++ b/docs/TCF Service - System Monitor.html @@ -0,0 +1,654 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services - System Monitor</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services - System Monitor</h1> + +<ul> + <li><a href='#Cmds'>Commands</a> + <ul> + <li><a href='#CmdGetContext'>Get Context</a> + <li><a href='#CmdGetChildren'>Get Children</a> + <li><a href='#CmdGetCommandLine'>Get Command Line</a> + <li><a href='#CmdGetEnvironment'>Get Environment</a> + </ul> + <li><a href='#Events'>Events</a> + <li><a href='#API'>API</a> +</ul> + +<h1>System Monitor Service</h1> + +<p>The service can be used for monitoring system activity and utilization. +It provides list of running processes, different process attributes like command line, environment, etc., +and some resource utilization data. The service can be used by a client to provide functionality +similar to Unix 'top' utility or Windows 'Task Manager'.</p> + +<p>Command and event parameters are encoded +as zero terminated <a href='TCF Specification.html#JSON'>JSON</a> strings.</p> + +<p>The service uses standard format for error reports, +see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p> + +<h2><a name='Cmds'>Commands</a></h2> + +<h3><a name='CmdGetContext'>Get Context</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • SysMonitor • getContext • <i><string: context ID></i> • +</font></b></pre> + +<p>The command retrieves context info for given context ID. +A context corresponds to an execution thread or process. +Context IDs are valid across TCF services, so it is allowed to issue 'SysMonitor.getContext' +command with a context that was obtained from another service.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><context data></i> • + +<i><context data></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><object></i> +</font></b></pre> + +<p>Context data object should, at least, contain member +<b><font face="Courier New" size=2 color=#333399>"ID" : <i><string>.</i></font></b> +</p> + +<p>Predefined context properties are:</p> +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b></code> + - ID of the context, same as getContext command argument. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ParentID" : <i><string></i></font></b></code> + - parent context ID. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CWD" : <i><string></i></font></b></code> + - current working directory of the process. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Root" : <i><string></i></font></b></code> + - the process's root directory (as set by chroot). + + <li><code><b><font face="Courier New" size=2 color=#333399>"UID" : <i><int></i></font></b></code> + - User ID of the process owner. + + <li><code><b><font face="Courier New" size=2 color=#333399>"UGID" : <i><int></i></font></b></code> + - Group ID of the process owner. + + <li><code><b><font face="Courier New" size=2 color=#333399>"UserName" : <i><string></i></font></b></code> + - user name of the process owner. + + <li><code><b><font face="Courier New" size=2 color=#333399>"GroupName" : <i><string></i></font></b></code> + - group name of the process owner. + + <li><code><b><font face="Courier New" size=2 color=#333399>"PID" : <i><int></i></font></b></code> + - system process ID. + + <li><code><b><font face="Courier New" size=2 color=#333399>"File" : <i><string></i></font></b></code> + - executable file of the process. + + <li><code><b><font face="Courier New" size=2 color=#333399>"State" : <i><string></i></font></b></code> + - one character from the string "RSDZTW" where R is running, S is + sleeping in an interruptible wait, D is waiting in uninterruptible + disk sleep, Z is zombie, T is traced or stopped (on a signal), and W + is paging. + + <li><code><b><font face="Courier New" size=2 color=#333399>"PPID" : <i><int></i></font></b></code> + - system ID of the parent process. + + <li><code><b><font face="Courier New" size=2 color=#333399>"PGRP" : <i><int></i></font></b></code> + - the process group ID of the process. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Session" : <i><int></i></font></b></code> + - the session ID of the process. + + <li><code><b><font face="Courier New" size=2 color=#333399>"TTY" : <i><int></i></font></b></code> + - the tty the process uses. + + <li><code><b><font face="Courier New" size=2 color=#333399>"TGID" : <i><int></i></font></b></code> + - the process group ID of the process which currently owns the tty that + the process is connected to. + + <li><code><b><font face="Courier New" size=2 color=#333399>"TracerPID" : <i><int></i></font></b></code> + - ID of a process that has attached this process for tracing or debugging. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Flags" : <i><int></i></font></b></code> + - the kernel flags word of the process. Details depend on the kernel. + + <li><code><b><font face="Courier New" size=2 color=#333399>"MinFlt" : <i><int></i></font></b></code> + - the number of minor faults the process has made which have not + required loading a memory page from disk. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CMinFlt" : <i><int></i></font></b></code> + - the number of minor faults that the process's waited-for children have made. + + <li><code><b><font face="Courier New" size=2 color=#333399>"MajFlt" : <i><int></i></font></b></code> + - the number of major faults the process has made which have required + loading a memory page from disk. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CMajFlt" : <i><int></i></font></b></code> + - the number of major faults that the process's waited-for children + have made. + + <li><code><b><font face="Courier New" size=2 color=#333399>"UTime" : <i><int></i></font></b></code> + - the number of milliseconds that this process has been scheduled in user mode. + + <li><code><b><font face="Courier New" size=2 color=#333399>"STime" : <i><int></i></font></b></code> + - the number of milliseconds that this process has been scheduled in kernel mode. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CUTime" : <i><int></i></font></b></code> + - the number of jiffies that this process's waited-for children have + been scheduled in user mode. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CSTime" : <i><int></i></font></b></code> + - the number of jiffies that this process's waited-for children have + been scheduled in user mode. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Priority" : <i><int></i></font></b></code> + - the standard nice value. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Nice" : <i><int></i></font></b></code> + - the nice value. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ITRealValue" : <i><int></i></font></b></code> + - the time in milliseconds before the next SIGALRM is sent to the process + due to an interval timer. + + <li><code><b><font face="Courier New" size=2 color=#333399>"StartTime" : <i><int></i></font></b></code> + - the time in milliseconds the process started after system boot. + + <li><code><b><font face="Courier New" size=2 color=#333399>"VSize" : <i><int></i></font></b></code> + - virtual memory size in bytes. + + <li><code><b><font face="Courier New" size=2 color=#333399>"PSize" : <i><int></i></font></b></code> + - memory pages size in bytes. + + <li><code><b><font face="Courier New" size=2 color=#333399>"RSS" : <i><int></i></font></b></code> + - resident Set Size: number of pages the process has in real memory, + minus used for administrative purposes. This is just the pages which + count towards text, data, or stack space. This does not include + pages which have not been demand-loaded in, or which are swapped out. + + <li><code><b><font face="Courier New" size=2 color=#333399>"RLimit" : <i><int></i></font></b></code> + - current limit in bytes on the rss of the process. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CodeStart" : <i><int></i></font></b></code> + - the address above which program text can run. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CodeEnd" : <i><int></i></font></b></code> + - the address below which program text can run. + + <li><code><b><font face="Courier New" size=2 color=#333399>"StackStart" : <i><int></i></font></b></code> + - the address of the start of the stack. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Signals" : <i><int></i></font></b></code> + - the bitmap of pending signals. + + <li><code><b><font face="Courier New" size=2 color=#333399>"SigBlock" : <i><int></i></font></b></code> + - the bitmap of blocked signals. + + <li><code><b><font face="Courier New" size=2 color=#333399>"SigIgnore" : <i><int></i></font></b></code> + - the bitmap of ignored signals. + + <li><code><b><font face="Courier New" size=2 color=#333399>"SigCatch" : <i><int></i></font></b></code> + - the bitmap of caught signals. + + <li><code><b><font face="Courier New" size=2 color=#333399>"WChan" : <i><int></i></font></b></code> + - this is the "channel" in which the process is waiting. It is the + address of a system call, and can be looked up in a namelist if you + need a textual name. + + <li><code><b><font face="Courier New" size=2 color=#333399>"NSwap" : <i><int></i></font></b></code> + - number of pages swapped. + + <li><code><b><font face="Courier New" size=2 color=#333399>"CNSwap" : <i><int></i></font></b></code> + - cumulative NSwap for child processes. + + <li><code><b><font face="Courier New" size=2 color=#333399>"ExitSignal" : <i><int></i></font></b></code> + - signal to be sent to parent when this process exits. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Processor" : <i><int></i></font></b></code> + - CPU number last executed on. + + <li><code><b><font face="Courier New" size=2 color=#333399>"RTPriority" : <i><int></i></font></b></code> + - real-time scheduling priority. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Policy" : <i><int></i></font></b></code> + - scheduling policy. +</ul> + +<h3><a name='CmdGetChildren'>Get Children</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • SysMonitor • getChildren • <i><string: parent context ID></i> • +</font></b></pre> + +<p>The command requests a list of contexts available for System Monitor commands.</p> + +<p>Parent context ID can be null – to retrieve top level of the hierarchy, can be one +of context IDs retrieved by previous getChildren commands, or it can be obtained from another service. +Contexts hierarchy can be simple plain list or it can form a tree. It is up to target agent developers to +choose layout that is most descriptive for a given target.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of context IDs></i> • + +<i><array of context IDs></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><context ID list></i> ] + +<i><context ID list></i> + <font face=Wingdings>Ø</font> <i><string: context ID></i> + <font face=Wingdings>Ø</font> <i><context ID list></i> , <i><string: context ID></i> +</font></b></pre> + +<h3><a name='CmdGetCommandLine'>Get Command Line</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • SysMonitor • getCommandLine • <i><string: context ID></i> • +</font></b></pre> + +<p>The command requests a list of progess command line arguments.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of string></i> • + +<i><array of string></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> [ ] + <font face=Wingdings>Ø</font> [ <i><string list></i> ] + +<i><string list></i> + <font face=Wingdings>Ø</font> <i><string></i> + <font face=Wingdings>Ø</font> <i><string list></i> , <i><string></i> +</font></b></pre> + +<h3><a name='CmdGetEnvironment'>Get Environment</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • SysMonitor • getEnvironment • <i><string: context ID></i> • +</font></b></pre> + +<p>The command requests a list of progess environment variables.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • <i><array of string></i> • +</font></b></pre> + +<h2><a name='Events'>Events</a></h2> + +<p>No events are currently defined for System Monitor service.</p> + +<h2><a name='API'>API</a></h2> + +<pre> +<font color=#7F0055>public interface</font> ISysMonitor <font color=#7F0055>extends</font> IService { + + <font color=#7F0055>static final</font> String NAME = "SysMonitor"; + + <font color=#3F5FBF>/** + * Retrieve context info for given context ID. + * + * <font color=#7F9FBF>@param</font> id – context ID. + * <font color=#7F9FBF>@param</font> done - callback interface called when operation is completed. + */</font> + IToken getContext(String id, DoneGetContext done); + + <font color=#3F5FBF>/** + * Client callback interface for getContext(). + */</font> + <font color=#7F0055>interface</font> DoneGetContext { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context – context data. + */</font> + <font color=#7F0055>void</font> doneGetContext(IToken token, Exception error, SysMonitorContext context); + } + + <font color=#3F5FBF>/** + * Retrieve children of given context. + * + * <font color=#7F9FBF>@param</font> parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * <font color=#7F9FBF>@param</font> done - callback interface called when operation is completed. + */</font> + IToken getChildren(String parent_context_id, DoneGetChildren done); + + <font color=#3F5FBF>/** + * Client callback interface for getChildren(). + */</font> + <font color=#7F0055>interface</font> DoneGetChildren { + <font color=#3F5FBF>/** + * Called when contexts data retrieval is done. + * <font color=#7F9FBF>@param</font> error – error description if operation failed, null if succeeded. + * <font color=#7F9FBF>@param</font> context_ids – array of available context IDs. + */</font> + <font color=#7F0055>void</font> doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + <font color=#3F5FBF>/** + * Context property names. + */</font> + <font color=#7F0055>static final</font> String + <font color=#3F5FBF>/** The TCF context ID */</font> + PROP_ID = "ID", + + <font color=#3F5FBF>/** The TCF parent context ID */</font> + PROP_PARENTID = "ParentID", + + <font color=#3F5FBF>/** Current working directory of the process */</font> + PROP_CWD = "CWD", + + <font color=#3F5FBF>/** The process's root directory (as set by chroot) */</font> + PROP_ROOT = "Root", + + <font color=#3F5FBF>/** User ID of the process owner */</font> + PROP_UID = "UID", + + <font color=#3F5FBF>/** Group ID of the process owner */</font> + PROP_UGID = "UGID", + + <font color=#3F5FBF>/** User name of the process owner */</font> + PROP_USERNAME = "UserName", + + <font color=#3F5FBF>/** Group name of the process owner */</font> + PROP_GROUPNAME = "GroupName", + + <font color=#3F5FBF>/** System process ID */</font> + PROP_PID = "PID", + + <font color=#3F5FBF>/** Executable file of the process */</font> + PROP_FILE = "File", + + <font color=#3F5FBF>/** One character from the string "RSDZTW" where R is running, S is + * sleeping in an interruptible wait, D is waiting in uninterruptible + * disk sleep, Z is zombie, T is traced or stopped (on a signal), and W + * is paging.*/</font> + PROP_STATE = "State", + + <font color=#3F5FBF>/** System ID of the parent process */</font> + PROP_PPID = "PPID", + + <font color=#3F5FBF>/** The process group ID of the process */</font> + PROP_PGRP = "PGRP", + + <font color=#3F5FBF>/** The session ID of the process */</font> + PROP_SESSION = "Session", + + <font color=#3F5FBF>/** The tty the process uses */</font> + PROP_TTY = "TTY", + + <font color=#3F5FBF>/** The process group ID of the process which currently owns the tty that + * the process is connected to. */</font> + PROP_TGID = "TGID", + + <font color=#3F5FBF>/** ID of a process that has attached this process for tracing or debugging */</font> + PROP_TRACERPID = "TracerPID", + + <font color=#3F5FBF>/** The kernel flags word of the process. Details depend on the kernel */</font> + PROP_FLAGS = "Flags", + + <font color=#3F5FBF>/** The number of minor faults the process has made which have not + * required loading a memory page from disk */</font> + PROP_MINFLT = "MinFlt", + + <font color=#3F5FBF>/** The number of minor faults that the process's waited-for children have made */</font> + PROP_CMINFLT = "CMinFlt", + + <font color=#3F5FBF>/** The number of major faults the process has made which have required + * loading a memory page from disk */</font> + PROP_MAJFLT = "MajFlt", + + <font color=#3F5FBF>/** The number of major faults that the process's waited-for children + * have made */</font> + PROP_CMAJFLT = "CMajFlt", + + <font color=#3F5FBF>/** The number of milliseconds that this process has been scheduled in user mode */</font> + PROP_UTIME = "UTime", + + <font color=#3F5FBF>/** The number of milliseconds that this process has been scheduled in kernel mode */</font> + PROP_STIME = "STime", + + <font color=#3F5FBF>/** The number of jiffies that this process's waited-for children have + * been scheduled in user mode */</font> + PROP_CUTIME = "CUTime", + + <font color=#3F5FBF>/** The number of jiffies that this process's waited-for children have + * been scheduled in user mode */</font> + PROP_CSTIME = "CSTime", + + <font color=#3F5FBF>/** The standard nice value */</font> + PROP_PRIORITY = "Priority", + + <font color=#3F5FBF>/** The nice value */</font> + PROP_NICE = "Nice", + + <font color=#3F5FBF>/** The time in milliseconds before the next SIGALRM is sent to the process + * due to an interval timer */</font> + PROP_ITREALVALUE = "ITRealValue", + + <font color=#3F5FBF>/** The time in milliseconds the process started after system boot */</font> + PROP_STARTTIME = "StartTime", + + <font color=#3F5FBF>/** Virtual memory size in bytes */</font> + PROP_VSIZE = "VSize", + + <font color=#3F5FBF>/** Memory pages size in bytes */</font> + PROP_PSIZE = "PSize", + + <font color=#3F5FBF>/** Resident Set Size: number of pages the process has in real memory, + * minus used for administrative purposes. This is just the pages which + * count towards text, data, or stack space. This does not include + * pages which have not been demand-loaded in, or which are swapped out */</font> + PROP_RSS = "RSS", + + <font color=#3F5FBF>/** Current limit in bytes on the rss of the process */</font> + PROP_RLIMIT = "RLimit", + + <font color=#3F5FBF>/** The address above which program text can run */</font> + PROP_CODESTART = "CodeStart", + + <font color=#3F5FBF>/** The address below which program text can run */</font> + PROP_CODEEND = "CodeEnd", + + <font color=#3F5FBF>/** The address of the start of the stack */</font> + PROP_STACKSTART = "StackStart", + + <font color=#3F5FBF>/** The bitmap of pending signals */</font> + PROP_SIGNALS = "Signals", + + <font color=#3F5FBF>/** The bitmap of blocked signals */</font> + PROP_SIGBLOCK = "SigBlock", + + <font color=#3F5FBF>/** The bitmap of ignored signals */</font> + PROP_SIGIGNORE = "SigIgnore", + + <font color=#3F5FBF>/** The bitmap of caught signals */</font> + PROP_SIGCATCH = "SigCatch", + + <font color=#3F5FBF>/** This is the "channel" in which the process is waiting. It is the + * address of a system call, and can be looked up in a name list if you + * need a textual name */</font> + PROP_WCHAN = "WChan", + + <font color=#3F5FBF>/** Number of pages swapped */</font> + PROP_NSWAP = "NSwap", + + <font color=#3F5FBF>/** Cumulative NSwap for child processes */</font> + PROP_CNSWAP = "CNSwap", + + <font color=#3F5FBF>/** Signal to be sent to parent when this process exits */</font> + PROP_EXITSIGNAL = "ExitSignal", + + <font color=#3F5FBF>/** CPU number last executed on */</font> + PROP_PROCESSOR = "Processor", + + <font color=#3F5FBF>/** Real-time scheduling priority */</font> + PROP_RTPRIORITY = "RTPriority", + + <font color=#3F5FBF>/** Scheduling policy */</font> + PROP_POLICY = "Policy"; + + + <font color=#3F5FBF>/** + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + */</font> + <font color=#7F0055>interface</font> SysMonitorContext { + + <font color=#3F5FBF>/** + * Get context ID. + * Same as getProperties().get(“ID”) + */</font> + String getID(); + + <font color=#3F5FBF>/** + * Get parent context ID. + * Same as getProperties().get(“ParentID”) + */</font> + String getParentID(); + + <font color=#3F5FBF>/** + * Get process group ID. + * Same as getProperties().get(“PGRP”) + */</font> + <font color=#7F0055>long</font> getPGRP(); + + <font color=#3F5FBF>/** + * Get process ID. + * Same as getProperties().get(“PID”) + */</font> + <font color=#7F0055>long</font> getPID(); + + <font color=#3F5FBF>/** + * Get process parent ID. + * Same as getProperties().get(“PPID”) + */</font> + <font color=#7F0055>long</font> getPPID(); + + <font color=#3F5FBF>/** + * Get process TTY group ID. + * Same as getProperties().get(“TGID”) + */</font> + <font color=#7F0055>long</font> getTGID(); + + <font color=#3F5FBF>/** + * Get tracer process ID. + * Same as getProperties().get(“TracerPID”) + */</font> + <font color=#7F0055>long</font> getTracerPID(); + + <font color=#3F5FBF>/** + * Get process owner user ID. + * Same as getProperties().get(“UID”) + */</font> + <font color=#7F0055>long</font> getUID(); + + <font color=#3F5FBF>/** + * Get process owner user name. + * Same as getProperties().get(“UserName”) + */</font> + String getUserName(); + + <font color=#3F5FBF>/** + * Get process owner user group ID. + * Same as getProperties().get(“UGID”) + */</font> + <font color=#7F0055>long</font> getUGID(); + + <font color=#3F5FBF>/** + * Get process owner user group name. + * Same as getProperties().get(“GroupName”) + */</font> + String getGroupName(); + + <font color=#3F5FBF>/** + * Get process state. + * Same as getProperties().get(“State”) + */</font> + String getState(); + + <font color=#3F5FBF>/** + * Get process virtual memory size in bytes. + * Same as getProperties().get(“VSize”) + */</font> + <font color=#7F0055>long</font> getVSize(); + + <font color=#3F5FBF>/** + * Get process virtual memory page size in bytes. + * Same as getProperties().get(“PSize”) + */</font> + <font color=#7F0055>long</font> getPSize(); + + <font color=#3F5FBF>/** + * Get number of memory pages in process resident set. + * Same as getProperties().get(“RSS”) + */</font> + <font color=#7F0055>long</font> getRSS(); + + <font color=#3F5FBF>/** + * Get context executable file. + * Same as getProperties().get(“File”) + */</font> + String getFile(); + + <font color=#3F5FBF>/** + * Get context current file system root. + * Same as getProperties().get(“Root”) + */</font> + String getRoot(); + + <font color=#3F5FBF>/** + * Get context current working directory. + * Same as getProperties().get(“CWD”) + */</font> + String getCurrentWorkingDirectory(); + + <font color=#3F5FBF>/** + * Get all available context properties. + * @return Map 'property name' -> 'property value' + */</font> + Map<String,Object> getProperties(); + } + + <font color=#3F5FBF>/** + * Get context command line. + */</font> + IToken getCommandLine(String id, DoneGetCommandLine done); + + <font color=#7F0055>interface</font> DoneGetCommandLine { + <font color=#7F0055>void</font> doneGetCommandLine(IToken token, Exception error, String[] cmd_line); + } + + <font color=#3F5FBF>/** + * Get context environment variables. + */</font> + IToken getEnvironment(String id, DoneGetEnvironment done); + + <font color=#7F0055>interface</font> DoneGetEnvironment { + <font color=#7F0055>void</font> doneGetEnvironment(IToken token, Exception error, String[] environment); + } +} +</pre> + +</body> +</html> diff --git a/docs/TCF Services.html b/docs/TCF Services.html new file mode 100644 index 000000000..38377555c --- /dev/null +++ b/docs/TCF Services.html @@ -0,0 +1,108 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Services</title> +</head> + +<body lang='EN-US'> + +<h1>Target Communication Framework Services</h1> + +<p>Copyright (c) 2007 Wind River Systems, Inc. Made available under the EPL v1.0 +<p>Direct comments, questions to the <a href="mailto:dsdp-tm-dev@eclipse.org">dsdp-tm-dev@eclipse.org</a> mailing list + +<h2>Table of Contents</h2> +<ul> + <li><a href='#Overview'>Overview</a> + <li><a href='#Syntax'>Syntax Rules Notation</a> + <li><a href='#ErrorFormat'>Error Report Format</a> + <li><a href='#Services'>Services</a> +</ul> + +<h2><a name='Overview'>Overview</a></h2> + +TCF communication model is based on the idea of services. A service is a group of related commands, events and semantics. +For example, <a href='TCF Service - Memory.html'>Memory Service</a> defines group of command and events for +reading and writing target memory. +Service definitions are not part of the <a href='TCF Specification.html'>framework specification</a>, and new services +are expected to be defined by developers of tools and target agents. +Defenitions of standard services are provided to achieve certain level of compatibility between tools and targets. + +<h2><a name='Syntax'>Syntax Rules Notation</a></h2> + +<p>Format of the protocol messages is defined by syntax rules. Syntax is described +using a simple variant of Backus-Naur Form. In particular:</p> + +<ul type='disc'> + <li>Italic lower case words in a courier font, enclosed into angular brackets, are + used to denote syntactic categories, for example: <b><i><font face="Courier New" size=2 color=#333399><token>. + </font></i></b>Category name can be followed by colon and a text, which explains semantics + of the category, for example: <b><i><font face="Courier New" size=2 color=#333399><int: + error code></font></i></b> has same meaning as <b><i><font face="Courier New" size=2 color=#333399><int></font></i></b>, + but denotes that the integer number used to indicate an "error code". + + <li>A syntax rule consists of a category designation followed by one or more syntax + definitions for the category. The category name and each definition are placed on + separate lines, bullets are used to denote definitions, for example: +</ul> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><chars></i> + <font face=Wingdings>Ø</font> <i><char></i> + <font face=Wingdings>Ø</font> <i><chars> <char></i> +</font></b></pre> + +<ul type='disc'> + <li>Spaces are added for readability only and they are not part of the syntax. + + <li>All text in the category definition, other than categories and spaces, is UTF-8 + based representation of a message bytes. + + <li>The symbol ‘•’ designates a zero byte. +</ul> + +<h2><a name='ErrorFormat'>Error Report Format</a></h2> + +<p>Most of TCF standard services use same format for error reporting:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><error report></i> + <font face=Wingdings>Ø</font> <i><int: error code></i> • <i><error description></i> +</font></b></pre> + +<p>Error code zero means success. Error description provides a short, localizable, +human readable explanation of error.</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><error description></i> + <font face=Wingdings>Ø</font> null + <font face=Wingdings>Ø</font> <i><string></i> + <font face=Wingdings>Ø</font> { "format" : <i><string></i> , "params" : [ <i><params></i> ] } + +<i><params></i> + <font face=Wingdings>Ø</font> <i><value></i> + <font face=Wingdings>Ø</font> <i><params></i> , <i><value></i> +</font></b></pre> + +<p>For <b><i><font face="Courier New" size=2 color=#333399><string></font></i></b> +and <b><i><font face="Courier New" size=2 color=#333399><value></font></i></b> encoding see +<a href='TCF Specification.html#JSON'>JSON - Preferred Marshaling</a>. +Error description format supports separation between constant and variable parts +of the message ("format" and "params"). This is done to support localization. See +Java class <b><font face="Courier New" size=2>java.text.MessageFormat</font></b> for details.</p> + +<h2><a name='Services'>Services</h2> +<ul> + <li><a href='TCF Service - Memory.html'>Memory Service</a> + <li><a href='TCF Service - Processes.html'>Processes Service</a> + <li><a href='TCF Service - Run Control.html'>Run Control Service</a> + <li><a href='TCF Service - Registers.html'>Registers Service</a> + <li><a href='TCF Service - Stack Trace.html'>Stack Trace Service</a> + <li><a href='TCF Service - Breakpoints.html'>Breakpoints Service</a> + <li><a href='TCF Service - File System.html'>File System Service</a> + <li><a href='TCF Service - System Monitor.html'>System Monitor Service</a> +</ul> + +</body> +</html> +
\ No newline at end of file diff --git a/docs/TCF Specification Image1.png b/docs/TCF Specification Image1.png Binary files differnew file mode 100644 index 000000000..85933f613 --- /dev/null +++ b/docs/TCF Specification Image1.png diff --git a/docs/TCF Specification.html b/docs/TCF Specification.html new file mode 100644 index 000000000..2aec03f3f --- /dev/null +++ b/docs/TCF Specification.html @@ -0,0 +1,1407 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework Specification</title> +</head> +<body lang='EN-US'> + +<h1>Target Communication Framework Specification</h1> + +<p>Copyright (c) 2007 Wind River Systems, Inc. Made available under the EPL v1.0 +<p>Direct comments, questions to the <a href="mailto:dsdp-tm-dev@eclipse.org">dsdp-tm-dev@eclipse.org</a> mailing list + +<h1>Table of Contents</h1> + +<ul> + <li><a href='#Overview'>Overview</a> + <ul> + <li><a href='#Goals'>Goals</a> + <li><a href='#Definitions'>Definitions</a> + <li><a href='#Requirements'>Requirements</a> + <li><a href='#Syntax'>Syntax Rules Notation</a> + </ul> + <li><a href='#Design'>Framework Software Design Considerations</a> + <ul> + <li><a href='#Concurrency'>Concurrency</a> + <li><a href='#Reflection'>Reflection</a> + <li><a href='#Ordering'>Message ordering</a> + </ul> + <li><a href='#Transport'>Transport Layer</a> + <li><a href='#Protocol'>Communication Protocol</a> + <ul> + <li><a href='#ProtocolCommands'>Commands</a> + <li><a href='#ProtocolResults'>Results</a> + <li><a href='#ProtocolEvents'>Events</a> + <li><a href='#ProtocolFlowControl'>Flow Control</a> + <li><a href='#ProtocolExamples'>Examples</a> + </ul> + <li><a href='#API'>API</a> + <li><a href='#JSON'>JSON - Preferred Marshaling</a> + <ul> + <li><a href='#JSONExamples'>JSON - Examples</a> + </ul> + <li><a href='#Locator'>Locator Service</a> + <ul> + <li><a href='#LocatorPeer'>Peer Attributes</a> + <li><a href='#LocatorCommands'>Locator Service Commands</a> + <li><a href='#LocatorEvents'>Locator Service Events</a> + <li><a href='#LocatorAPI'>Locator Service API</a> + </ul> +</ul> + +<h1><a name='Overview'>Overview</a></h1> + +<p>Today almost every device software development tool on the market has its own method +of communication with target system. Communication methods often require individual setup, +configuration and maintenance, impose unnecessary limitations. +Target Communication Framework goal is to establish common ground in +the area of communication protocols between development tools and embedded devices.</p> + +<p>The goal is a single protocol used to communicate between all tools and targets:</p> +<p><img src='TCF Specification Image1.png'></p> + +<h2><a name='Goals'>Goals</a></h2> + +<ul type='disc'> + <li>Universal, simple, lightweight, vendor agnostic framework for tools and targets + to communicate for purpose of debugging, profiling, code patching and other device + software development needs. + + <li>Single configuration per target (not per tool per target as today in most cases), + or no configuration when possible. + + <li>Minimal overhead and footprint on target side. +</ul> + +<h2><a name='Definitions'>Definitions</a></h2> + +<dl> +<dt><b>Peer:</b> <dd>communication endpoint. Both hosts and targets are called peers. A +peer can act as a client or a server depending on services it implements. + +<dt><b>Service:</b> <dd>group of related commands, events and semantic define a service. +A service can be discovered, added or removed as a group at communication endpoint. + +<dt><b>Message:</b> <dd>a packet of data, formatted according to framework specification +and transmitted over communication channel. + +<dt><b>Channel:</b> <dd>communication link connecting two endpoints (peers). A single +channel may be used to communicate with multiple services. Multiple channels may +be used to connect the same peers, however no command or event ordering is guaranteed +across channels. + +<dt><b>Command:</b> <dd>command is a message sent to remote peer in order to request some +predefined action there. + +<dt><b>Result:</b> <dd>result is a message sent as a response to a command. + +<dt><b>Event:</b> <dd>event is a message sent to all interested parties in order to notify +them about state changes. +</dl> + +<h2><a name='Requirements'>Requirements</a></h2> + +<ul type='disc'> + <li>Simple and extensible protocol. + + <li>Small footprint on the target. + + <li>Fully asynchronous, message based communication. + + <li>Two ways of message routing: + + <ul> + <li>Point to point request/response (command/result) communication. + + <li>Subscription based broadcast of notifications (events). + </ul> + + <li>Full duplex, symmetric communication: both host and target should be able to send + commands and events at same time, though ability to establish communication channel + can be limited to host only. + + <li>For each communication channel between two peers, the framework should preserve + order of commands, results and events. + + <li>Support for slow and high latency connections. + + <li>Transport protocol agnostic. The framework should work well, at least, on top + of: TCP/IP, UDP, USB, RS232 and JTAG. + + <li>The framework should support multiplexing, that is, single target device shared + between multiple tools at same time. To reduce footprint on the target, multiplexing + can be implemented on host if needed. + + <li>Dynamic discovery of participating targets and hosts. No configuration when possible. + + <li>Dynamic discovery of available services (high level protocols, command sets). + Clients can query for available services. + + <li>Services can be added and removed dynamically. + + <li>Framework should define a set of common high level interfaces (services). For + example: flow control, memory access, registers access, up-load mechanism, kernel + awareness, run control, target file system, console, flash programming. Implementation + of these interfaces is optional, but if provided it will support much wider compatibility + with various tools. + + <li>Framework should be layered in such a way so it is possible to use different transport + medias (e.g. TCP/IP, RS232, USB, etc) without any changes to individual services. + In other words, transport implementation should be services agnostic, and services + implementation should be transport agnostic. + + <li>Each service defines how marshalling is done for command, result and event arguments. + This allows existing target agents to remain unchanged. + + <li>Framework should define a preferred marshalling mechanism that new services can + use. + + <li>The definition of services (groups of related commands and events) is separate + from the definition of the framework itself. The framework provides unified communication + mechanism, while services use it to communicate with its clients. + + <li>Anybody (including 3rd parties) can add services without having to modify communication + protocol or framework software. + + <li>The framework should support tunneling through a proxy. Proxy may be used, for + example: + + <ul type='circle'> + <li>to bridge different transport protocols, like TCP and RS232; + + <li>to make a RS232 or USB target connection accessible from multiple hosts; + + <li>to access targets behind firewalls or otherwise not directly accessible + </ul> + + <li>A proxy should be able to provide services in addition to those implemented by + a target. Such distribution of services allows target services to be implemented on + a host, thereby reducing the footprint on the target. For example, debug information, + stack back trace or OS awareness can be implemented by a proxy on a host. To provide + this functionality, proxy services would typically use low-level target services, + like memory access. + + <li>Supports of concurrent requests. Maximum number of concurrent requests (window + size) can be limited on target side. Simple agents only have to support window size + of 1. Framework should maintain a queue of additional requests, so tools don’t need + to know the window size. This may only be relevant for certain transport protocols + e.g. UDP. + + <li>Events can be broadcasted at any time, i.e. no polling should be required. + + <li>Protocol should support a standard mechanism of sending data larger than MTU. +</ul> + +<h2><a name='Syntax'>Syntax Rules Notation</a></h2> + +<p>Format of the protocol messages is defined by syntax rules. Syntax is described +using a simple variant of Backus-Naur Form. In particular:</p> + +<ul type='disc'> + <li>Italic lower case words in a courier font, enclosed into angular brackets, are + used to denote syntactic categories, for example: <b><i><font face="Courier New" size=2 color=#333399><token>. + </font></i></b>Category name can be followed by colon and a text, which explains semantics + of the category, for example: <b><i><font face="Courier New" size=2 color=#333399><int: + error code></font></i></b> has same meaning as <b><i><font face="Courier New" size=2 color=#333399><int></font></i></b>, + but denotes that the integer number used to indicate an “error code”. + + <li>A syntax rule consists of a category designation followed by one or more syntax + definitions for the category. The category name and each definition are placed on + separate lines, bullets are used to denote definitions, for example: + <pre><b><font face="Courier New" size=2 color=#333399> + <i><chars></i> + <font face=Wingdings>Ø</font> <i><char></i> + <font face=Wingdings>Ø</font> <i><chars> <char></i> + </font></b></pre> + + <li>Spaces are added for readability only and they are not part of the syntax. + + <li>All text in the category definition, other then categories and spaces, is UTF-8 + based representation of a message bytes. + + <li>The symbol ‘•’ designates a zero byte. +</ul> + +<h1><a name='Design'>Framework Software Design Considerations</a></h1> + +<p>The framework will be packaged, distributed and installed on a host as separate +product. It should be installed as system service and require no configuration for +most common case – target connected over TCP or UDP on a local network. For more complicated +setup, framework should have easily accessible and user friendly GUI with all relevant +configuration options.</p> + +<p>Framework should use a dynamic discovery protocol to locate targets and other hosts +running instances of the framework when possible, and maintain a dynamic list of available +communication endpoints, as well as lists of services available at each endpoint. +Host discovery is needed to locate hosts able to proxy communications for targets, +which are not accessible otherwise - for example, targets connected with RS232 or +JTAG to a remote host. It should also be possible to add target configuration manually. +Development tools will access this data through the Locator Service API and use it, +for example, to present a user a list of available targets that have capabilities +needed by a particular tool.</p> + +<p>Framework should provide software libraries to be used by tools and target agents +developers. The libraries should be available at least for ANSI C and Java. On host +side, at least Windows, Solaris and Linux must be supported. Libraries will provide +APIs for low-level communication protocol, Locator Service, preferred marshaling and +predefined common services.</p> + +<p>The proposed target communication protocol is text-based. It allows extensions, +which define messages with blocks of binary data, but it is not a recommended data +formatting, and its usage is supposed to be limited. Text-based protocols have both +advantages and disadvantages in compare with binary protocols.</p> + +<p>Advantages:</p> + +<ul type='disc'> + <li>The software for text-based protocols is easier to develop and debug since they + use a relatively human-friendly communication. + + <li>It is possible to use huge selection of existing tools and library routines to + view, edit, validate, and transform text-based data. + + <li>Text based definition is in line with current trend in Internet protocols: most + popular protocols such as SMTP and HTTP are text-based. +</ul> + +<p>Disadvantages:</p> + +<ul type='disc'> + <li>Text-based protocols usually need more bytes to store numerical data than binary + protocols do. + + <li>Parsing of text-based data is not efficient compared to parsing of binary data + since text-based data is usually not stored in a way similar to how it is stored in + computer memory. + + <li>It is seldom possible to read only part of a text-based message since the exact + byte offset to a data item is generally not known. +</ul> + +<p>A possible alternative to consider is binary, variable length encoding like BaseStream.</p> + +<h2><a name='Concurrency'>Concurrency</a></h2> + +<p>Concurrent asynchronous communication is much faster then synchronous, because +it alleviates communication channel latency and allows better bandwidth utilization. +But it also requires proper design of framework software. Concurrency, in general, +implies multithreading. However, systems developed with global multithreading, are +often unreliable and prone to different kinds of thread synchronization problems, +which are often very difficult to locate and resolve. We therefore strongly recommend +that the software is designed to follow the compartment threading model, which simplifies +thread synchronization and promotes reliable software design. In this model each thread +execution path is strictly contained in predefined subset of code (compartment), and +no code, except for reentrant libraries, is executed by multiple threads. Each compartment +has a message queue and other threads communicate with the compartment thread by posting +messages to the queue.</p> + +<p>Framework APIs are designed to be compatible with the compartment threading model. +Hence the API functions do not contain any thread synchronization primitives to protect +against multiple threads using the functions. All framework APIs belong to a single +compartment and should be used by a single thread. The same thread is used to dispatch +events and command results. Concurrency is achieved by declaring API functions to +be asynchronous. Asynchronous functions do not have any return value, and returns +immediately, most of the time before the intended job is done. They take additional +arguments to specify a callback function and callback data. In object-oriented languages +such as Java, this is typically done by a single callback object argument containing +both the data and the function. The result listener is called asynchronously when +the job is done. This approach is commonly known as asynchronous<b>, </b>event-driven<b> +</b>or<b> </b>callback-based<b> </b>programming<b>.</b></p> + +<p>One important characteristic of an asynchronous code is that the methods defined +by the user will often be called from within the framework itself, rather than from +the user's application code. The framework often plays the role of the main program +in coordinating and sequencing application activity. This phenomenon is called Inversion +of Control (also known as the Hollywood Principle - "Don't call us, we'll call you").</p> + +<h2><a name='Reflection'>Reflection</a></h2> + +<p>Communication between development tools and embedded devices must allow a host +to collect target side data and build a reflection of target state. Reflection is +usually incomplete – a subset of all remote data. Reflection is always delayed – it +represents a remote peer state in the past. Reflection can be updated by polling for +data changes or by listening to events (event is communication message that is sent +asynchronously by a peer to notify others about state change). Reflection is correct +if it represents a state that actually did happen on remote peer.</p> + +<p>Reflection is coherent if it is exactly equal to subset of peer state at a single +moment of time and that moment of time is not too far in the past. Non-coherent reflection +can have parts of data representing peer state at different moments of time. Coherent +reflection is more valuable for a user, because non-coherent reflection can have logically +conflicting data if that data was collected at different time.</p> + +<p>Traditionally, debuggers would ensure coherence of state reflection by collecting +data only while target is suspended, and flushing all (or most) reflection data (reducing +observed subset to zero) when target is resumed. This approach does not work well +for multithreaded, multicore or real time targets. Maintaining correctness and coherence +of a non-empty reflection while target is running requires additional support from +target agent, communication software and debugger itself.</p> + +<p>Since remote peer state is changing over time, coherent reflection can be built +only if:</p> + +<ul type='disc'> + <li>Observed subset of state is properly selected and dynamically re-selected. Observing + too much can overflow communication channel. Observing too little has little value + for a user. + + <li>Observer is listening to all relevant events. + + <li>Events are coming in exactly same order as corresponding changes happen. + + <li>Events are properly ordered relative to other messages that carry state data. + + <li>All changes in observed subset of peer state are reported by events. + + <li>All event messages must either contain a complete description of a change or they + all should not contain any state data at all. If client is getting some data from + events and required to retrieve new values of other changed data by using other means + (commands), the reflection will not be coherent at least until such retrieval is complete. + And if such periods of data retrieval overlap, the reflection will never be coherent. + Sending deltas with events is usually more efficient then using data retrieval commands + to update reflection. +</ul> + +<h2><a name='Ordering'>Message ordering</a></h2> + +<p>The transmission order of commands, results and events is important, it coveys +valuable information about target state transitions and it should be preserved when +possible. Consider an example:</p> + +<p>Client transmits: </p> + +<pre> + Command X=2 +</pre> + +<p>Then, as result of some activity of another client or the target itself, X is assigned +value 3.</p> + +<p>Target transmits:</p> + +<pre> + Event X=3 + Result X=2 +</pre> + +<p>Now client has to show value of X to a user. If the order of messages is preserved, +the client will know that command was executed <i>after</i> X was assigned 3, the +last message contains last known value of X and 2 is the correct value to show. If +the target is allowed to transmit events and results in arbitrary order, the client +will have no clue what to show – 2 or 3. In fact, the client will have to make a tough +decision about each message it receives: either trust message data as correct last +known target state, or assume the message came in out-of-order and ignore it, or re-request +the information from the target.</p> + +<p>Note that re-requesting data from the target, in general, does not solve the problem +of interpretation of messages when order is not preserved. For example, after sending +a request to read value of X, X could change at about the same time, and client could +receive:</p> + +<pre> + Event X=2 + Result X=3 + Event X=4 +</pre> + +<p>If order is not preserved, it is still impossible to tell which value of X is the +last one. A client could assume value of X unknown every time it receives a notification +of X change, and then re-request the data again. But this is expensive and, if events +coming in frequently, client can end up in infinite loop re-requesting the data again +and again, and it will never have trustworthy data about current target state.</p> + +<p>Developers should be careful when using multithreading or multiple queues in software +design – it can easily cause message reordering.</p> + +<p>The framework itself is required to preserve message order. However, if for whatever +reason a target agent cannot preserve message order, the result will be that clients +of the service can receive messages in the wrong order. When this is the case it should +be well documented, so tools developers are aware and can make the best of the situation. +In most cases it will not cause any trouble, but there is no perfect way to restore +actual sequence of events and maintain data coherency after ordering was lost, and +in some cases it can severely impact tool functionality and user experience.</p> + +<h1><a name='Transport'>Transport Layer</a></h1> + + +<p>Tools are required to be transport protocol agnostic, so most of the layer functionality +is used internally by framework and is not exposed to clients. This layer maintains +a collection of transport protocol handlers. Each handler is designed to provide:</p> + +<ul type='disc'> + <li>Enumeration of available peers, including both automatically discovered and manually + configured peers. Handler fires notification events when peers are added or removed. + Enumeration can be implemented by scanning JTAG chain, by broadcasting special UDP + packet and waiting for responses, by communicating with ICE hardware, or by any other + suitable means. + + <li>Bidirectional point-to-point communication of data packets. Packets are arrays + of bytes of arbitrary size. + Transport handler and underlying protocol are responsible for adding all necessary + control data, headers, error checking bits, addresses, fragmentation/defragmentation, + flow control, transmission retries and whatever necessary to ensure lossless, order-preserving + delivery of packets. + + <li>Configuration UI should allow user to inspect and modify properties of both manually + configured and automatically discovered peers, setup new peers, view connections status + and statistics. +</ul> + +<p>Existing service discovery protocols can be used together with the framework, for +example:</p> + +<ul type='disc'> + <li>Zero Configuration Networking (Zeroconf), see <a href='http://www.zeroconf.org/'>http://www.zeroconf.org</a>; + + <li>Service Location Protocol (SLP), developed by the IETF; + + <li>Jini, which is Sun’s Java-base approach to service discovery, see <a href='http://www.sun.com/jini'>http://www.sun.com/jini</a>; + + <li>Salutation, developed by an open industry consortium, called the Salutation Consortium; + + <li>Microsoft’s Universal Plug and Play (UPnP), see <a href='http://www.upnp.org/'>http://www.upnp.org</a>; + + <li>Bluetooth Service Discovery Protocol (SDP). +</ul> + +<p>Service discovery protocols, as well as transport protocols will be supported by +framework plug-ins, they are not part of framework code itself, and they can be developed +by 3rd parties. Note that existing discovery protocols define term “service” differently +- as an independent communication endpoint (usually a TCP/IP port). In this document +it is called “peer” (host, target, communication endpoint), and a peer can provide +multiple services over single communication channel.</p> + +<p>Using of standard discovery protocols should be optional, because it can potentially +cause conflict or interference between development tools and application being developed +over a use of same standard protocol – devices software often includes implementation +of service discovery protocols as part of application code to support their main functions. +</p> + +<h1><a name='Protocol'>Communication Protocol</a></h1> + +<p>The communication protocol defines data packets properties and roles common for +all services. The communication protocol API provides functions for opening and /closing +of the communication channel for a particular peer, and for sending and receiving +data packets. The protocol define contents of a part of a packet, the rest of the +packet is treated as array of bytes at this level. The communication protocol implementation +also provides:</p> + +<ul type='disc'> + <li>Multiplexing – opening multiple channels per peer. + + <li>Proxy – packet forwarding in behalf of other hosts. +</ul> + +<p>Protocol defines three packet types: commands (requests), results (responses), +and events. Each packet consists of several protocol defined control fields followed +by byte array of data. Binary representation of control fields is a sequence of zero +terminated ASCII strings. Format of data array depends on a service. We recommend +using framework preferred marshaling for data formatting.</p> + +<p>Syntax:</p> +<pre><b><font face="Courier New" size=2 color=#333399> +<i><message></i> + <font face=Wingdings>Ø</font> <i><command></i> + <font face=Wingdings>Ø</font> <i><result></i> + <font face=Wingdings>Ø</font> <i><event></i> + <font face=Wingdings>Ø</font> <i><flow control message></i> +</font></b></pre> + +<h2><a name='ProtocolCommands'>Commands</a></h2> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><command></i> + <font face=Wingdings>Ø</font> C • <i><token> </i>• <i><service name> </i>• <i><command name> </i>• <i><byte array: arguments></i> +</font></b></pre> + +<p>Command packets start with string “C”.</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><token></i> + <font face=Wingdings>Ø</font> <i><chars></i> +</font></b></pre> + +<p>Token is unique string generated by framework for each command. It is used to match +results to commands.</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><service name></i> + <font face=Wingdings>Ø</font> <i><chars></i> +</font></b></pre> + +<p>Service name is used to identify a service that handles the command, it is same +string as returned by Service.getName().</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><command name></i> + <font face=Wingdings>Ø</font> <i><chars></i> +</font></b></pre> + +<p>Command name interpretation depends on a service.</p> + +<p>A command should always be answered with result packed. Result does not have to +be positive – it can include an error code, but there always must be one. Since client +cannot detect that a response is missing, if for some reasons peer is not able to +answer a command, it should consider such situation a fatal communication error and +it must shutdown the communication channel. It is not necessary to wait for result +before sending next command. In fact, sending multiple commands in a burst can greatly +improve performance, especially when connection has high latency. At the same time, +clients should be carefully designed to avoid flooding the communication channel with +unlimited number of requests, since this will use resources in forms of memory to +store the requests and time to process them.</p> + +<h2><a name='ProtocolResults'>Results</a></h2> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><result></i> + <font face=Wingdings>Ø</font> R • <i><token></i> • <i><byte array: result data></i> + <font face=Wingdings>Ø</font> P • <i><token></i> • <i><byte array: result data></i> +</font></b></pre> + +<p>Result packets start with string “P” for intermediate result and “R” for final +result. Receiving of “R” result concludes execution of corresponding command. +There should be exactly one “R” result for each command. In addition, command execution can produce any number of +intermediate “P” results. “P” results can be sent before “R”, and it can serve, for +example, as command execution progress report when execution takes long time.</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><token></i> + <font face=Wingdings>Ø</font> <i><chars></i> +</font></b></pre> + +<p>Token should match token field of one of the pending commands that produced the result.</p> + +<h2><a name='ProtocolEvents'>Events</a></h2> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><event></i> + <font face=Wingdings>Ø</font> E • <i><service name></i> • <i><event name></i> • <i><byte array: event data></i> +</font></b></pre> + +<p>Event packets start with string “E”.</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><service name></i> + <font face=Wingdings>Ø</font> <i><chars></i> +</font></b></pre> + +<p>Service name identifies a service that fired event, same string as returned by +Service.getName().</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><event name></i> + <font face=Wingdings>Ø</font> <i><chars></i> +</font></b></pre> + +<p>Event name meaning depends on a service.</p> + +<p>Events are used to notify clients about changes in peer state. Services should +provide sufficient variety of events for clients to track remote peer state without +too much of polling. Clients, interested in a particular aspect of the target state, +should have a “reflection” (or “model”) of that state and update the reflection by +listening for relevant events. If a service implements a command that changes a particular +aspect of peers state, then, normally, it should also generate notifications event +when that same part of the state changes and it should provide a command to retrieve +current value of the state – to be used by clients to initialize the reflection. Service +events are defined statically, together with commands. The framework does not do any +event processing besides delivering them to clients, however a service can define +additional event related functionality if necessary, for example, commands for event +filtering, enabling, disabling, registration, etc. Care should be taken when designing +events for a service - if events are sent too frequently, they will cause flooding +of the communication channels and degrade performance. However, too few events will +force clients to poll for changes and can also degrade performance. A balanced approach +is the best.</p> + +<h2><a name='ProtocolFlowControl'>Flow Control</a> </h2> + +<p>It often happens that one side of communication channel produces messages faster +then they can be transmitted over the channel or can be consumed by another side. +This will cause channel traffic congestion (flooding). Framework will deal with the +problem and slow down transmitting side by blocking execution inside sendEvent(), +sendCommand() and sendResult() functions when message buffers are full. However, in +many cases, it is not the best way to handle congestion. For example, it can make +a tool UI appear locked for prolonged time or it can break target software if it is +designed to work in real time. Clients can use flow control events to implement advanced +techniques to handle traffic congestion, for example, message coalescing, switching +to less detailed messages, etc.</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><flow control message></i> + <font face=Wingdings>Ø</font> F • <i><int: traffic congestion level></i> • +</font></b></pre> + +<p>Traffic congestion level value is in range –100..100, where –100 means no pending +messages (no traffic), 0 means optimal load, and positive numbers +indicate level of congestion. When a peer receives flow control message with congestion level > 0 +it should try to reduce its transmition speed.</p> + +<h2><a name='ProtocolExamples'>Message Examples</a></h2> + +<p>Examples use simplified command arguments and result data. See service description +for actual data formats.</p> + +<p>Executing <b><i>suspend</i></b> command from <b><i>RunControl</i></b> service:</p> + +<p> </p> + +<pre> +Send : C 1 RunControl suspend “Thread1” +Receive: E RunControl suspended “Thread1” +Receive: R 1 “Success” +</pre> + +<p>Same command, but target was already suspended:</p> + +<pre> +Receive: E RunControl suspended “Thread1” +… +Send : C 2 RunControl suspend “Thread1” +Receive: R 2 “Already suspended” +</pre> + +<p>Same command, but target was suspended (by another client) after sending the command, +but before command was executed: </p> + +<pre> +Receive: E RunControl running “Thread1” +… +Send : C 3 RunControl suspend “Thread1” +Receive: E RunControl suspended “Thread1” +Receive: R 3 “Already suspended” +</pre> + +<h2><a name='API'>Framework API</a></h2> + +<pre> +<font color=#3F5FBF>/** + * + * Class Protocol provides static methods to access Target Communication Framework root objects: + * 1. the framework event queue and dispatch thread; + * 2. local instance of Locator service, which maintains a list of available targets; + * 3. list of open communication channels. + */</font> +<font color=#7F0055>public class</font> Protocol { + + <font color=#7F0055>private static</font> IEventQueue <i>event_queue</i>; + + <font color=#3F5FBF>/** + * Before TCF can be used it should be given an object implementing IEventQueue interface. + * The implementation maintains a queue of objects implementing Runnable interface and + * executes <code>run</code> methods of that objects in a sequence by a single thread. + * The thread in referred as TCF event dispatch thread. Objects in the queue are called TCF events. + * Executing <code>run</code> method of an event is also called dispatching of event. + * + * Only few methods in TCF APIs are thread safe - can be invoked from any thread. + * If a method description does not say "can be invoked from any thread" explicitly - + * the method must be invoked from TCF event dispatch thread. All TCF listeners are + * invoked from the dispatch thread. + * + * <font color=#7F9FBF>@param</font> event_queue - IEventQueue implementation. + */</font> + <font color=#7F0055>public static void</font> setEventQueue(IEventQueue event_queue); + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> instance of IEventQueue that should be used for TCF events. + */</font> + <font color=#7F0055>public static</font> IEventQueue getEventQueue(); + + <font color=#3F5FBF>/** + * Returns true if the calling thread is TCF dispatch thread. + * Use this call to ensure that a given task is being executed (or not being) + * on dispatch thread. + * This method is thread-safe. + * + * <font color=#7F9FBF>@return</font> true if running on the dispatch thread. + */</font> + <font color=#7F0055>public static boolean</font> isDispatchThread(); + + <font color=#3F5FBF>/** + * Causes runnable to have its run + * method called in the dispatch thread of the framework. + * Runnables are dispatched in same order as queued. + * If invokeLater is called from the dispatching thread + * the <i>runnable.run()</i> will still be deferred until + * all pending events have been processed. + * + * This method can be invoked from any thread. + * + * <font color=#7F9FBF>@param runnable</font> the Runnable whose run + * method should be executed asynchronously.</font> + */</font> + <font color=#7F0055>public static void</font> invokeLater(Runnable runnable); + + <font color=#3F5FBF>/** + * Causes runnable to have its run + * method called in the dispatch thread of the framework. + * Calling thread is suspended util the method is executed. + * This method is thread-safe. + * + * <font color=#7F9FBF>@param runnable</font> the Runnable whose run + * method should be executed on dispatch thread. + */</font> + <font color=#7F0055>public static void</font> invokeAndWait(Runnable runnable) + <font color=#7F0055>throws</font> InterruptedException; + + <font color=#3F5FBF>/** + * Get instance of the framework locator service. + * The service can be used to discover available remote peers. + * + * @return instance of ILocator. + */</font> + <font color=#7F0055>public static</font> ILocator getLocator(); + + <font color=#3F5FBF>/** + * Return an array of all open channels. + * @return an array of IChannel + */</font> + <font color=#7F0055>public static</font> IChannel[] getOpenChannels(); + + <font color=#3F5FBF>/** + * Interface to be implemented by clients willing to be notified when + * new TCF communication channel is opened. + */</font> + <font color=#7F0055>public interface</font> ChannelOpenListener { + <font color=#7F0055>public void</font> onChannelOpen(IChannel channel); + } + + <font color=#3F5FBF>/** + * Add a listener that will be notified when new channel is opened. + * @param listener + */</font> + <font color=#7F0055>public static void</font> addChannelOpenListener(ChannelOpenListener listener); + + <font color=#3F5FBF>/** + * Remove channel opening listener. + * @param listener + */</font> + <font color=#7F0055>public static void</font> removeChannelOpenListener(ChannelOpenListener listener); + + <font color=#3F5FBF>/** + * Transmit TCF event message. + * The message is sent to all open communication channels – broadcasted. + */</font> + <font color=#7F0055>public static void</font> sendEvent(String service, String name, byte[] data); + + <font color=#3F5FBF>/** + * Call back after TCF messages sent by this host up to this moment are delivered + * to their intended target. This method is intended for synchronization of messages + * across multiple channels. + * + * Note: Cross channel synchronization can reduce performance and throughput. + * Most clients don't need cross channel synchronization and should not call this method. + * + * @param done will be executed by dispatch thread after communication + * messages are delivered to corresponding targets. + */</font> + <font color=#7F0055>public static void</font> sync(Runnable done); +} + +<font color=#3F5FBF>/** + * IChannel represents communication link connecting two endpoints (peers). + * The channel asynchroniously transmits messages: commands, results and events. + * A single channel may be used to communicate with multiple services. + * Multiple channels may be used to connect the same peers, however no command or event + * ordering is guaranteed across channels. + */</font> +<font color=#7F0055>public interface</font> IChannel { + + <font color=#3F5FBF>/** + * Channel state IDs + */</font> + <font color=#7F0055>static final</font> int + <i><font color=#0000C0>STATE_OPENNING</font></i> = 0, + <i><font color=#0000C0>STATE_OPEN</font></i> = 1, + <i><font color=#0000C0>STATE_CLOSED</font></i> = 2; + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> channel current state, see STATE_* + */</font> + int getState(); + + <font color=#3F5FBF>/** + * Send command message to remote peer for execution. Commands can be queued + * locally before transmission. Sending commands too fast can fill up + * communication channel buffers. Calling thread will be blocked until + * enough buffer space is freed up by transmitting pending messages. + */</font> + IToken sendCommand(IService service, String name, <font color=#7F0055>byte</font>[] args, + ICommandListener done); + + <font color=#3F5FBF>/** + * Command listener interface. Clients implement this interface + * to receive command results. + */</font> + <font color=#7F0055>interface</font> ICommandListener { + + <font color=#3F5FBF>/** + * Called when progress message (intermediate result) is received + * from remote peer. + */</font> + <font color=#7F0055>void</font> progress(<font color=#7F0055>byte</font>[] data); + + <font color=#3F5FBF>/** + * Called when command result received from remote peer. + */</font> + <font color=#7F0055>void</font> result(<font color=#7F0055>byte</font>[] data); + } + + <font color=#3F5FBF>/** + * Send result message to remote peer. Messages can be queued locally before + * transmission. Sending messages too fast can fill up communication channel + * buffers. Calling thread will be blocked until enough buffer space is + * freed up by transmitting pending messages. + */</font> + <font color=#7F0055>void</font> sendResult(IToken token, <font color=#7F0055>byte</font>[] results); + + <font color=#3F5FBF>/** + * Get current level of outbound traffic congestion. + * + * <font color=#7F9FBF>@return</font> integer value in range –100..100, where –100 means no pending + * messages (no traffic), 0 means optimal load, and positive numbers + * indicate level of congestion. + * + * Note: inbound traffic congestion is detected by framework and reported to + * remote peer without client needed to be involved. + */</font> + int getCongestion(); + + <font color=#3F5FBF>/** + * Channel listener interface. + */</font> + <font color=#7F0055>interface</font> IChannelListener { + + <font color=#3F5FBF>/** + * Notifies listeners about congestion level changes. When level > 0 + * client should delay sending more messages. + */</font> + <font color=#7F0055>void</font> congestionLevel(int level); + } + + <font color=#3F5FBF>/** + * Subscribe a channel listener. The listener will be notified about changes of + * outbound traffic congestion level. + */</font> + <font color=#7F0055>void</font> addChannelListener(IChannelListener listener); + + <font color=#3F5FBF>/** + * Remove a channel listener. + */</font> + <font color=#7F0055>void</font> removeChannelListener(IChannelListener listener); + + <font color=#3F5FBF>/** + * Command server interface. + * This interface is to be implemented by service providers. + */</font> + <font color=#7F0055>interface</font> ICommandServer { + + <font color=#3F5FBF>/** + * Called every time a command is received from remote peer. + */</font> + <font color=#7F0055>void</font> command(IToken token, String name, <font color=#7F0055>byte</font>[] data); + } + + <font color=#3F5FBF>/** + * Subscribe a command server. The server will be notified about command + * messages received through this channel for given service. + */</font> + <font color=#7F0055>void</font> addCommandServer(IService service, ICommandServer listener); + + <font color=#3F5FBF>/** + * Remove a command server. + */</font> + <font color=#7F0055>void</font> removeCommandServer(IService service, ICommandServer listener); + + <font color=#3F5FBF>/** + * A generic interface for service event listener. + * Services usually define a service specific event listener interface, + * which is implemented using this generic listener. + * Service clients should use service specific listener interface, + * unless no such interface is defined. + */</font> + <font color=#7F0055>interface</font> IEventListener { + <font color=#7F0055>void</font> event(String name, <font color=#7F0055>byte</font>[] data); + } + + <font color=#3F5FBF>/** + * Subscribe an event message listener for given service. + */</font> + <font color=#7F0055>void</font> addEventListener(IService service, IEventListener listener); + + <font color=#3F5FBF>/** + * Unsubscribe an event message listener for given service. + */</font> + <font color=#7F0055>void</font> removeEventListener(IService service, IEventListener listener); + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> IPeer object representing local endpoint of communication channel. + */</font> + IPeer getLocalPeer(); + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> IPeer object representing remote endpoint of communication channel. + */</font> + IPeer getRemotePeer(); + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> map of services available on local peer. It maps service names to + * IService implemetations. + */</font> + Map<String, IService> getLocalServices(); + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> map of services available on removte peer. It maps service names to + * IService implemetations. + */</font> + Map<String, IService> getRemoteServices(); + + <font color=#3F5FBF>/** + * Close communication channel. + */</font> + <font color=#7F0055>void</font> close(); + + <font color=#3F5FBF>/** + * Close channel in case of communication error. + * <font color=#7F9FBF>@param error</font> + */</font> + <font color=#7F0055>void</font> terminate(Throwable error); + + <font color=#3F5FBF>/** + * Redirect this channel to given peer using this channel remote peer + * locator service as a proxy. + * <font color=#7F9FBF>@param peer_id</font> + */</font> + <font color=#7F0055>void</font> redirect(String peer_id); +} + + +<font color=#3F5FBF>/** + * Object implemeting IToken interface is created by framework for every + * command sent over communication channel. It is used to match command to its + * results, and also can be used to cancel commands. + */</font> +<font color=#7F0055>public</font> interface IToken { + + <font color=#3F5FBF>/** + * Try to cancel a command associated with given token. A command can be + * canceled by this method only if it was not transmitted yet to remote peer + * for execution. Successfully canceled command does not produce any result + * messages. + * + * <font color=#7F9FBF>@return</font> true if successful. + */</font> + <font color=#7F0055>boolean</font> cancel(); +} + +</pre> + +<h1><a name='JSON'>Preferred Marshaling</a></h1> + +<p>TCF messages data format is service specific. Since services specifications are +separate from protocol specification, a service designer can choose any data format that +suits the service requirements best. However, to promote better compatibility and to +simplify service design and implementation, we recommend to use <b>JSON</b> for data formatting.</p> + +<p><b>JSON</b> (pronounced like the +English given name <i>Jason</i>), which stands for "<b>J</b>ava<b>S</b>cript <b>O</b>bject +<b>N</b>otation", is a lightweight, text-based, language-independent computer data +interchange format. <b>JSON</b> is a subset of the object literal notation of JavaScript +but its use does not require JavaScript.</p> + +<p><b>JSON</b> represents data with the same basic types that programming languages +use. <b>JSON</b>'s basic types are:</p> + +<ul type='disc'> + <li>Number (integer, real, or floating-point) + + <li>String (double-quoted with backslash escapement) + + <li>Boolean (<code>true</code> and <code>false</code>) + + <li>Array (an ordered sequence of values) + + <li>Object (collection of key/value pairs) + + <li><code>null</code> + </ul> + +<p>The structures used in most programming languages easily map directly onto JSON's +structures, and back again.</p> + +<p>JSON maps data onto Unicode string. Then the string is mapped onto array of bytes +using UTF-8 encoding.</p> + +<p>JSON specification:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +<i><object></i> + <font face=Wingdings>Ø</font> {} + <font face=Wingdings>Ø</font> { <i><members></i> } + +<i><members></i> + <font face=Wingdings>Ø</font> <i><string></i> : <i><value></i> + <font face=Wingdings>Ø</font> <i><members></i> , <i><string></i> : <i><value></i> + +<i><array></i> + <font face=Wingdings>Ø</font> [] + <font face=Wingdings>Ø</font> [ <i><elements></i> ] + +<i><elements></i> + <font face=Wingdings>Ø</font> <i><value></i> + <font face=Wingdings>Ø</font> <i><elements</i>> , <i><value></i> + +<i><value></i> + <font face=Wingdings>Ø</font> <i><string></i> + <font face=Wingdings>Ø</font> <i><number></i> + <font face=Wingdings>Ø</font> <i><object></i> + <font face=Wingdings>Ø</font> <i><array></i> + <font face=Wingdings>Ø</font> <i><boolean></i> + <font face=Wingdings>Ø</font> null + +<i><boolean></i> + <font face=Wingdings>Ø</font> true + <font face=Wingdings>Ø</font> false + +<i><string></i> + <font face=Wingdings>Ø</font> "" + <font face=Wingdings>Ø</font> " <i><chars></i> " + +<i><chars></i> + <font face=Wingdings>Ø</font> <i><char></i> + <font face=Wingdings>Ø</font> <i><chars> <char></i> + +<i><char</i>> + <font face=Wingdings>Ø</font> <i><any Unicode except " or \ or control></i> + <font face=Wingdings>Ø</font> \"<i></i> + <font face=Wingdings>Ø</font> \\<i></i> + <font face=Wingdings>Ø</font> \/<i></i> + <font face=Wingdings>Ø</font> \b<i></i> + <font face=Wingdings>Ø</font> \f<i></i> + <font face=Wingdings>Ø</font> \n<i></i> + <font face=Wingdings>Ø</font> \r<i></i> + <font face=Wingdings>Ø</font> \t<i></i> + <font face=Wingdings>Ø</font> \u <i><four-hex-digits></i> + +<i><number</i>> + <font face=Wingdings>Ø</font> <i><int></i> + <font face=Wingdings>Ø</font> <<i>int> <fraction></i> + <font face=Wingdings>Ø</font> <<i>int> <exponent></i> + <font face=Wingdings>Ø</font> <<i>int> <fraction> <exponent></i> + +<i><int></i> + <font face=Wingdings>Ø</font> <i><digit></i> + <font face=Wingdings>Ø</font> <<i>digit 1-9> <digits></i> + <font face=Wingdings>Ø</font> - <<i>digit></i> + <font face=Wingdings>Ø</font> - <<i>digit 1-9> <digits</i>> + +<i><fraction></i> + <font face=Wingdings>Ø</font> . <i><digits></i> + +<i><exponent></i> + <font face=Wingdings>Ø</font> <i><e></i> <i><digits></i> + +<i><digits></i> + <font face=Wingdings>Ø</font> <i><digit></i> + <font face=Wingdings>Ø</font> <<i>digits></i> <<i>digit></i> + +<i><e></i> + <font face=Wingdings>Ø</font> e + <font face=Wingdings>Ø</font> e+ + <font face=Wingdings>Ø</font> e- + <font face=Wingdings>Ø</font> E + <font face=Wingdings>Ø</font> E+ + <font face=Wingdings>Ø</font> E- + +</font></b></pre> + +<p>See <a href='http://www.json.org/'>www.json.org</a> for more details.</p> + +<h2><a name='JSONExamples'>Examples</a></h2> + +<p>This is a JSON array containing two objects:</p> + +<pre> + [ + { + "Precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "Precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] +</pre> + +<h1><a name='Locator'>Locator Service</a></h1> + +<p>Locator Service uses transport layer to search for peers and to collect data about +peer’s attributes and capabilities (services). Discovery mechanism depends on transport +protocol and is part of that protocol handler. Targets, known by other hosts, are +added to local list of peers. <font color=red>Security? </font>Automatically discovered +targets require no further configuration. Additional targets can be configured manually.</p> + +<p>All TCF peers must implement Locator service. The implementation is part of the framework itself. +It is the only required service, all other services are optional and, formally, not part of the framework.</p> + +<h2><a name='LocatorPeer'>Peer Atributes</a></h2> + +<p><i><object: peer data></i> is collection of peer attributes. It should, at least, contain member +<b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b>. +It can also contain a number of components describing peer properties and capabilities. +Predefined attributes are:</p> + +<ul> + <li><code><b><font face="Courier New" size=2 color=#333399>"ID" : <i><string></i></font></b></code> + - ID of the peer. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Name" : <i><string></i></font></b></code> + - human readable peer name. + + <li><code><b><font face="Courier New" size=2 color=#333399>"OSName" : <i><string></i></font></b></code> + - peer OS name, if applicable. + + <li><code><b><font face="Courier New" size=2 color=#333399>"TransportName" : <i><string></i></font></b></code> + - name of a trasport protocol to use to connect to this peer, for example: TCP. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Host" : <i><string></i></font></b></code> + - peer host name, if transport is TCP or UDP. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Aliases" : <i><string></i></font></b></code> + - peer host name aliases, if transport is TCP or UDP. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Addresses" : <i><string></i></font></b></code> + - peer IP addresses, if transport is TCP or UDP. + + <li><code><b><font face="Courier New" size=2 color=#333399>"Port" : <i><string></i></font></b></code> + - peer port number, if transport is TCP or UDP. +</ul> + +<p>Most clients dont need to know peer attributes other then ID and Name. Clients are expected to call IPeer.openChannel() +method and let the framework to check peers attributes and create appropriate communication cahnnel that is best suited for +communication with the peer. After a channel is established, a client can learn peer capabilities by looking +at services it implements (use IChannel.getRemoteServices() method to get a map of services).</p> + +<h2><a name='LocatorCommands'>Locator Service Commands</a></h2> + +<h3><a name='LocatorCommandRedirect'>redirect</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Locator • redirect • <i><string: peer ID></i> • +</font></b></pre> + +<p>The command redirects the channel to become connected to given peer. +Locator service starts acting as a proxy.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • <i><error report></i> • +</font></b></pre> + +<h3><a name='LocatorCommandSync'>sync</a></h3> + +<pre><b><font face="Courier New" size=2 color=#333399> +C • <i><token></i> • Locator • sync • +</font></b></pre> + +<p>Sync command does nothing and simply returns back an empty result. The command is used for +cross channel synchronization. Since commands are executed in order they were issued, by waiting +for sync result a client makes sure that all commands, that were issued before sync, are fully processed.</p> + +<p>Reply:</p> + +<pre><b><font face="Courier New" size=2 color=#333399> +R • <i><token></i> • +</font></b></pre> + +<h2><a name='LocatorEvents'>Locator Service Events</a></h2> + +<pre><b><font face="Courier New" size=2 color=#333399> +E • Locator • Hello • <i><array: service names></i> • +E • Locator • peerAdded • <i><object: peer data></i> • +E • Locator • peerChanged • <i><object: peer data></i> • +E • Locator • peerRemoved • <i><string: peer ID></i> • +</font></b></pre> + +<dl> + <dt><b>Hello</b> + <dd>is the first message sent by the framework after establishing a communication channel. + The message lets other side of the channel to know capabilities of this peer. + Message data consists of an array of service names that are provided by the peer. + Service names list is a complete and unambiguous declaration of peer's capabilities. + To avoid ambiguity, different services (even slightly different, like versions of same service) + must have different names. Framework delays all other communications between peers until exchange + of Hello messages is complete. + <dt><b>peerAdded</b> + <dd>is sent when the service discovers a new peer. + <dt><b>peerChanged</b> + <dd>is sent when peer attributes change. + <dt><b>peerRemoved</b> + <dd>is sent when the service deletes information about a peer. +</dl> + +<h2><a name='LocatorAPI'>Locator Service API</a></h2> + +<pre> +<font color=#3F5FBF>/** + * Base interface for all service interfaces. + * A client can get list of available services by + * calling IPeer.getLocalServices or IPeer.getRemoteServives + */</font> +<font color=#7F0055>public</font> interface IService { + + <font color=#3F5FBF>/** + * Get unique name of this service. + */</font> + String getName(); +} + +<font color=#3F5FBF>/** + * Both hosts and targets are represented by objects + * implementing IPeer interface. A peer can act as host or + * target depending on services it implements. + * List of currently known peers can be retrieved by + * calling ILocator.getPeers + */</font> +<font color=#7F0055>public interface</font> IPeer { + + <font color=#7F0055>static final</font> String + <i><font color=#0000C0>ATTR_ID</font></i> = <font color=#2A00FF>"ID"</font>, + <i><font color=#0000C0>ATTR_NAME</font></i> = <font color=#2A00FF>"Name"</font>, + <i><font color=#0000C0>ATTR_OS_NAME</font></i> = <font color=#2A00FF>"OSName"</font>, + <i><font color=#0000C0>ATTR_TRANSPORT_NAME</font></i> = <font color=#2A00FF>"TransportName"</font>, + <i><font color=#0000C0>ATTR_IP_HOST</font></i> = <font color=#2A00FF>"Host"</font>, + <i><font color=#0000C0>ATTR_IP_ALIASES</font></i> = <font color=#2A00FF>"Aliases"</font>, + <i><font color=#0000C0>ATTR_IP_ADDRESSES</font></i> = <font color=#2A00FF>"Addresses"</font>, + <i><font color=#0000C0>ATTR_IP_PORT</font></i> = <font color=#2A00FF>"Port"</font>; + + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> map of peer attributes + */</font> + Map<String, String> getAttributes(); + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> peer unique ID, same as getAttributes().get(ATTR_ID) + */</font> + String getID(); + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> peer name, same as getAttributes().get(ATTR_NAME) + */</font> + String getName(); + + <font color=#3F5FBF>/** + * Same as getAttributes().get(ATTR_OS_NAME) + */</font> + String getOSName(); + + <font color=#3F5FBF>/** + * Same as getAttributes().get(ATTR_TRANSPORT_NAME) + */</font> + String getTransportName(); + + <font color=#3F5FBF>/** + * Open channel to communicate with this peer. + * Note: the channel is not fully open yet when this method returns. + * It’s state is IChannel.STATE_OPENNING. + * Protocol.Listener will be called when the channel will be opened or closed. + */</font> + IChannel openChannel() <font color=#7F0055>throws</font> IOException; +} + +<font color=#3F5FBF>/** + * ILocator service uses transport layer to search for peers and to collect data about + * peer’s attributes and capabilities (services). Discovery mechanism depends on + * transport protocol and is part of that protocol handler. Targets, known by other + * hosts, are added to local list of peers. + * Automatically discovered targets require no further configuration. Additional targets + * can be configured manually. + * + * Clients should use Protocol.getLocator() to obtain local instance of ILocator, + * then ILocator.getPeers() can be used to get of available peers (hosts and targets). + */</font> +<font color=#7F0055>public interface</font> ILocator <font color=#7F0055>extends</font> IService { + + <font color=#7F0055>static final</font> String <i><font color=#0000C0>NAME</font></i> = <font color=#2A00FF>"Locator"</font>; + + <font color=#3F5FBF>/** + * Auto-configuration command and response codes. + */</font> + <font color=#7F0055>static final int</font> + <i><font color=#0000C0>CONF_REQ_INFO</font></i> = 1, + <i><font color=#0000C0>CONF_PEER_INFO</font></i> = 2; + + <font color=#3F5FBF>/** + * <font color=#7F9FBF>@return</font> Locator service name: "Locator" + */</font> + String getName(); + + <font color=#3F5FBF>/** + * Get map (ID -> IPeer) of available peers (hosts and targets). + * The method return cached (currently known to the framework) list of peers. + * The list is updated according to event received from transport layer + */</font> + Map<String,IPeer> getPeers(); + + <font color=#3F5FBF>/** + * Redirect this service channel to given peer using this service as a proxy. + */</font> + IToken redirect(String peer_id, DoneRedirect done); + + <font color=#7F0055>interface</font> DoneRedirect { + <font color=#7F0055>void</font> doneRedirect(IToken token, Exception error); + } + + <font color=#3F5FBF>/** + * Call back after TCF messages sent to this target up to this moment are delivered. + * This method is intended for synchronization of messages + * across multiple channels. + * + * Note: Cross channel synchronization can reduce performance and throughput. + * Most clients don't need channel synchronization and should not call this method. + * + * @param done will be executed by dispatch thread after communication + * messages are delivered to corresponding targets. + * + * This is internal API, TCF clients should use {@code com.windriver.tcf.api.protocol.Protocol}. + */</font> + IToken sync(DoneSync done); + + <font color=#7F0055>interface</font> DoneSync { + <font color=#7F0055>void</font> doneSync(IToken token); + } + + <font color=#3F5FBF>/** + * Add a listener for locator service events. + */</font> + <font color=#7F0055>void</font> addListener(Listener listener); + + <font color=#3F5FBF>/** + * Remove a listener for locator service events. + */</font> + <font color=#7F0055>void</font> removeListener(Listener listener); + + <font color=#7F0055>interface</font> Listener { + <font color=#7F0055>void</font> peerAdded(IPeer peer); + + <font color=#7F0055>void</font> peerRemoved(IPeer peer); + + <font color=#7F0055>void</font> peerChanged(IPeer peer); + } +} +</pre> + +</body> +</html> +
\ No newline at end of file diff --git a/docs/TCF_Launch_Dialog.jpg b/docs/TCF_Launch_Dialog.jpg Binary files differnew file mode 100644 index 000000000..837bd6f53 --- /dev/null +++ b/docs/TCF_Launch_Dialog.jpg diff --git a/docs/TCF_RSE_Files.jpg b/docs/TCF_RSE_Files.jpg Binary files differnew file mode 100644 index 000000000..c1088b0fc --- /dev/null +++ b/docs/TCF_RSE_Files.jpg diff --git a/docs/TCF_RSE_Processes.jpg b/docs/TCF_RSE_Processes.jpg Binary files differnew file mode 100644 index 000000000..e17a15817 --- /dev/null +++ b/docs/TCF_RSE_Processes.jpg diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 000000000..ce61a25d6 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html> +<head> + <title>Target Communication Framework</title> +</head> +<body> +<h1>Target Communication Framework </h1> + +<p>Copyright (c) 2007 Wind River Systems, Inc. Made available under the EPL v1.0 +<p>Direct comments, questions to the <a href="mailto:dsdp-tm-dev@eclipse.org">dsdp-tm-dev@eclipse.org</a> mailing list + +<h2>Available Documentation</h2> + +<table border=1 cellpadding=8> + <tr> + <td><a href='TCF Project.html'>TCF Project Overview</a> + <td width=500>TCF project goals and results + <tr> + <td><a href='TCF Getting Started.html'>TCF: Getting Started</a> + <td width=500>Getting started with TCF - creating Eclipse workspace, building agent, making a first connection + <tr> + <td><a href='TCF Specification.html'>TCF Specifications</a> + <td width=500>Design goals, requirements and format of TCF communication protocol, + framework API and software design considerations + <tr> + <td><a href='TCF Services.html'>TCF Services Definitions</a> + <td width=500>TCF communication model is based on the idea of services. + A service is a group of related commands, events and semantics. + New services are expected to be defined by developers of tools and target agents. + To achieve certain level of compatibility of tools/targets TCF inclides definitions + of common services + <tr> + <td><a href='TCF Context Identifier Explanation.html'>TCF Context Identifier Explanation</a> + <td width=500>Most if not all TCF services functions need some way to identify what entity e.g. process, + thread, task, device on JTAG scan chain, etc they should operate on. + To do this TCF uses a context identifier (aka ContextId). This document is attempting to explain how + ContextIds are intended to be used + <tr> + <td><a href='TCF Linux Agent Prototype.html'>TCF Agent Prototype</a> + <td width=500>Brief description of the TCF target agent prototype implementation +</table> + +</body> +</html> diff --git a/epl-v10.html b/epl-v10.html new file mode 100644 index 000000000..ed4b19665 --- /dev/null +++ b/epl-v10.html @@ -0,0 +1,328 @@ +<html xmlns:o="urn:schemas-microsoft-com:office:office" +xmlns:w="urn:schemas-microsoft-com:office:word" +xmlns="http://www.w3.org/TR/REC-html40"> + +<head> +<meta http-equiv=Content-Type content="text/html; charset=windows-1252"> +<meta name=ProgId content=Word.Document> +<meta name=Generator content="Microsoft Word 9"> +<meta name=Originator content="Microsoft Word 9"> +<link rel=File-List +href="./Eclipse%20EPL%202003_11_10%20Final_files/filelist.xml"> +<title>Eclipse Public License - Version 1.0</title> +<!--[if gte mso 9]><xml> + <o:DocumentProperties> + <o:Revision>2</o:Revision> + <o:TotalTime>3</o:TotalTime> + <o:Created>2004-03-05T23:03:00Z</o:Created> + <o:LastSaved>2004-03-05T23:03:00Z</o:LastSaved> + <o:Pages>4</o:Pages> + <o:Words>1626</o:Words> + <o:Characters>9270</o:Characters> + <o:Lines>77</o:Lines> + <o:Paragraphs>18</o:Paragraphs> + <o:CharactersWithSpaces>11384</o:CharactersWithSpaces> + <o:Version>9.4402</o:Version> + </o:DocumentProperties> +</xml><![endif]--><!--[if gte mso 9]><xml> + <w:WordDocument> + <w:TrackRevisions/> + </w:WordDocument> +</xml><![endif]--> +<style> +<!-- + /* Font Definitions */ +@font-face + {font-family:Tahoma; + panose-1:2 11 6 4 3 5 4 4 2 4; + mso-font-charset:0; + mso-generic-font-family:swiss; + mso-font-pitch:variable; + mso-font-signature:553679495 -2147483648 8 0 66047 0;} + /* Style Definitions */ +p.MsoNormal, li.MsoNormal, div.MsoNormal + {mso-style-parent:""; + margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman"; + mso-fareast-font-family:"Times New Roman";} +p + {margin-right:0in; + mso-margin-top-alt:auto; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman"; + mso-fareast-font-family:"Times New Roman";} +p.BalloonText, li.BalloonText, div.BalloonText + {mso-style-name:"Balloon Text"; + margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:8.0pt; + font-family:Tahoma; + mso-fareast-font-family:"Times New Roman";} +@page Section1 + {size:8.5in 11.0in; + margin:1.0in 1.25in 1.0in 1.25in; + mso-header-margin:.5in; + mso-footer-margin:.5in; + mso-paper-source:0;} +div.Section1 + {page:Section1;} +--> +</style> +</head> + +<body lang=EN-US style='tab-interval:.5in'> + +<div class=Section1> + +<p align=center style='text-align:center'><b>Eclipse Public License - v 1.0</b> +</p> + +<p><span style='font-size:10.0pt'>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER +THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, +REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE +OF THIS AGREEMENT.</span> </p> + +<p><b><span style='font-size:10.0pt'>1. DEFINITIONS</span></b> </p> + +<p><span style='font-size:10.0pt'>"Contribution" means:</span> </p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>a) +in the case of the initial Contributor, the initial code and documentation +distributed under this Agreement, and<br clear=left> +b) in the case of each subsequent Contributor:</span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>i) +changes to the Program, and</span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>ii) +additions to the Program;</span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>where +such changes and/or additions to the Program originate from and are distributed +by that particular Contributor. A Contribution 'originates' from a Contributor +if it was added to the Program by such Contributor itself or anyone acting on +such Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in conjunction +with the Program under their own license agreement, and (ii) are not derivative +works of the Program. </span></p> + +<p><span style='font-size:10.0pt'>"Contributor" means any person or +entity that distributes the Program.</span> </p> + +<p><span style='font-size:10.0pt'>"Licensed Patents " mean patent +claims licensable by a Contributor which are necessarily infringed by the use +or sale of its Contribution alone or when combined with the Program. </span></p> + +<p><span style='font-size:10.0pt'>"Program" means the Contributions +distributed in accordance with this Agreement.</span> </p> + +<p><span style='font-size:10.0pt'>"Recipient" means anyone who +receives the Program under this Agreement, including all Contributors.</span> </p> + +<p><b><span style='font-size:10.0pt'>2. GRANT OF RIGHTS</span></b> </p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>a) +Subject to the terms of this Agreement, each Contributor hereby grants Recipient +a non-exclusive, worldwide, royalty-free copyright license to<span +style='color:red'> </span>reproduce, prepare derivative works of, publicly +display, publicly perform, distribute and sublicense the Contribution of such +Contributor, if any, and such derivative works, in source code and object code +form.</span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>b) +Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide,<span style='color:green'> </span>royalty-free +patent license under Licensed Patents to make, use, sell, offer to sell, import +and otherwise transfer the Contribution of such Contributor, if any, in source +code and object code form. This patent license shall apply to the combination +of the Contribution and the Program if, at the time the Contribution is added +by the Contributor, such addition of the Contribution causes such combination +to be covered by the Licensed Patents. The patent license shall not apply to +any other combinations which include the Contribution. No hardware per se is +licensed hereunder. </span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>c) +Recipient understands that although each Contributor grants the licenses to its +Contributions set forth herein, no assurances are provided by any Contributor +that the Program does not infringe the patent or other intellectual property +rights of any other entity. Each Contributor disclaims any liability to Recipient +for claims brought by any other entity based on infringement of intellectual +property rights or otherwise. As a condition to exercising the rights and +licenses granted hereunder, each Recipient hereby assumes sole responsibility +to secure any other intellectual property rights needed, if any. For example, +if a third party patent license is required to allow Recipient to distribute +the Program, it is Recipient's responsibility to acquire that license before +distributing the Program.</span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>d) +Each Contributor represents that to its knowledge it has sufficient copyright +rights in its Contribution, if any, to grant the copyright license set forth in +this Agreement. </span></p> + +<p><b><span style='font-size:10.0pt'>3. REQUIREMENTS</span></b> </p> + +<p><span style='font-size:10.0pt'>A Contributor may choose to distribute the +Program in object code form under its own license agreement, provided that:</span> +</p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>a) +it complies with the terms and conditions of this Agreement; and</span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>b) +its license agreement:</span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>i) +effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose; </span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>ii) +effectively excludes on behalf of all Contributors all liability for damages, +including direct, indirect, special, incidental and consequential damages, such +as lost profits; </span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>iii) +states that any provisions which differ from this Agreement are offered by that +Contributor alone and not by any other party; and</span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>iv) +states that source code for the Program is available from such Contributor, and +informs licensees how to obtain it in a reasonable manner on or through a +medium customarily used for software exchange.<span style='color:blue'> </span></span></p> + +<p><span style='font-size:10.0pt'>When the Program is made available in source +code form:</span> </p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>a) +it must be made available under this Agreement; and </span></p> + +<p class=MsoNormal style='margin-left:.5in'><span style='font-size:10.0pt'>b) a +copy of this Agreement must be included with each copy of the Program. </span></p> + +<p><span style='font-size:10.0pt'>Contributors may not remove or alter any +copyright notices contained within the Program. </span></p> + +<p><span style='font-size:10.0pt'>Each Contributor must identify itself as the +originator of its Contribution, if any, in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution. </span></p> + +<p><b><span style='font-size:10.0pt'>4. COMMERCIAL DISTRIBUTION</span></b> </p> + +<p><span style='font-size:10.0pt'>Commercial distributors of software may +accept certain responsibilities with respect to end users, business partners +and the like. While this license is intended to facilitate the commercial use +of the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes the +Program in a commercial product offering, such Contributor ("Commercial +Contributor") hereby agrees to defend and indemnify every other +Contributor ("Indemnified Contributor") against any losses, damages and +costs (collectively "Losses") arising from claims, lawsuits and other +legal actions brought by a third party against the Indemnified Contributor to +the extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may participate +in any such claim at its own expense.</span> </p> + +<p><span style='font-size:10.0pt'>For example, a Contributor might include the +Program in a commercial product offering, Product X. That Contributor is then a +Commercial Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance claims and +warranties are such Commercial Contributor's responsibility alone. Under this +section, the Commercial Contributor would have to defend claims against the +other Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages.</span> </p> + +<p><b><span style='font-size:10.0pt'>5. NO WARRANTY</span></b> </p> + +<p><span style='font-size:10.0pt'>EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, +WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and distributing the +Program and assumes all risks associated with its exercise of rights under this +Agreement , including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs or +equipment, and unavailability or interruption of operations. </span></p> + +<p><b><span style='font-size:10.0pt'>6. DISCLAIMER OF LIABILITY</span></b> </p> + +<p><span style='font-size:10.0pt'>EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF +THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES.</span> </p> + +<p><b><span style='font-size:10.0pt'>7. GENERAL</span></b> </p> + +<p><span style='font-size:10.0pt'>If any provision of this Agreement is invalid +or unenforceable under applicable law, it shall not affect the validity or +enforceability of the remainder of the terms of this Agreement, and without +further action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable.</span> </p> + +<p><span style='font-size:10.0pt'>If Recipient institutes patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Program itself (excluding combinations of the Program with +other software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the date +such litigation is filed. </span></p> + +<p><span style='font-size:10.0pt'>All Recipient's rights under this Agreement +shall terminate if it fails to comply with any of the material terms or +conditions of this Agreement and does not cure such failure in a reasonable +period of time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive. </span></p> + +<p><span style='font-size:10.0pt'>Everyone is permitted to copy and distribute +copies of this Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The Agreement +Steward reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement Steward has +the right to modify this Agreement. The Eclipse Foundation is the initial +Agreement Steward. The Eclipse Foundation may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved.</span> </p> + +<p><span style='font-size:10.0pt'>This Agreement is governed by the laws of the +State of New York and the intellectual property laws of the United States of +America. No party to this Agreement will bring a legal action under this +Agreement more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation.</span> </p> + +<p class=MsoNormal><![if !supportEmptyParas]> <![endif]><o:p></o:p></p> + +</div> + +</body> + +</html>
\ No newline at end of file diff --git a/plugins/com.windriver.debug.tcf.core/.classpath b/plugins/com.windriver.debug.tcf.core/.classpath new file mode 100644 index 000000000..751c8f2e5 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/com.windriver.debug.tcf.core/.cvsignore b/plugins/com.windriver.debug.tcf.core/.cvsignore new file mode 100644 index 000000000..c5e82d745 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/.cvsignore @@ -0,0 +1 @@ +bin
\ No newline at end of file diff --git a/plugins/com.windriver.debug.tcf.core/.project b/plugins/com.windriver.debug.tcf.core/.project new file mode 100644 index 000000000..8a5b1f874 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.windriver.debug.tcf.core</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/plugins/com.windriver.debug.tcf.core/.settings/org.eclipse.jdt.core.prefs b/plugins/com.windriver.debug.tcf.core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..f06d39b5d --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +#Mon Sep 10 12:24:12 PDT 2007 +eclipse.preferences.version=1 +org.eclipse.jdt.core.builder.cleanOutputFolder=clean +org.eclipse.jdt.core.builder.duplicateResourceTask=warning +org.eclipse.jdt.core.builder.invalidClasspath=abort +org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore +org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,.svn/ +org.eclipse.jdt.core.circularClasspath=error +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 +org.eclipse.jdt.core.incompatibleJDKLevel=ignore +org.eclipse.jdt.core.incompleteClasspath=error diff --git a/plugins/com.windriver.debug.tcf.core/.settings/org.eclipse.ltk.core.refactoring.prefs b/plugins/com.windriver.debug.tcf.core/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 000000000..2bfce7b08 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,3 @@ +#Wed Apr 18 17:24:58 PDT 2007 +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/plugins/com.windriver.debug.tcf.core/META-INF/MANIFEST.MF b/plugins/com.windriver.debug.tcf.core/META-INF/MANIFEST.MF new file mode 100644 index 000000000..46a4bff9e --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/META-INF/MANIFEST.MF @@ -0,0 +1,15 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: com.windriver.debug.tcf.core;singleton:=true +Bundle-Version: 0.1.0 +Bundle-Activator: com.windriver.debug.tcf.core.TCFCore +Bundle-Vendor: %providerName +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.debug.core, + org.eclipse.core.resources, + com.windriver.tcf.api +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Eclipse-LazyStart: true +Export-Package: com.windriver.debug.tcf.core.model, + com.windriver.debug.tcf.core.launch diff --git a/plugins/com.windriver.debug.tcf.core/about.html b/plugins/com.windriver.debug.tcf.core/about.html new file mode 100755 index 000000000..6c5b3615b --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>January 10, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/plugins/com.windriver.debug.tcf.core/build.properties b/plugins/com.windriver.debug.tcf.core/build.properties new file mode 100644 index 000000000..e9863e281 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/plugins/com.windriver.debug.tcf.core/plugin.properties b/plugins/com.windriver.debug.tcf.core/plugin.properties new file mode 100644 index 000000000..d573d34eb --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2007 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Wind River Systems - initial implementation +############################################################################### +pluginName = TCF/Eclipse Debugger Integration Core +providerName = Eclipse.org + diff --git a/plugins/com.windriver.debug.tcf.core/plugin.xml b/plugins/com.windriver.debug.tcf.core/plugin.xml new file mode 100644 index 000000000..1a9590a32 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/plugin.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + <extension-point id="startup" name="TCF Startup" schema="schema/startup.exsd"/> + <extension + point="org.eclipse.debug.core.breakpoints"> + <breakpoint + markerType="com.windriver.debug.tcf.breakpoint.marker" + class="com.windriver.debug.tcf.core.model.TCFBreakpoint" + id="com.windriver.debug.tcf.breakpoint" + name="TCF Breakpoint"> + </breakpoint> + </extension> + <extension + id="com.windriver.debug.tcf.breakpoint.marker" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.debug.core.breakpointMarker"/> + <persistent value="true"/> + </extension> + <extension + point="org.eclipse.debug.core.launchConfigurationTypes"> + <launchConfigurationType + sourceLocatorId="com.windriver.debug.tcf.SourceLocator" + name="Target Communication Framework" + sourcePathComputerId="com.windriver.debug.tcf.SourcePathComputer" + delegate="com.windriver.debug.tcf.core.launch.TCFLaunchDelegate" + modes="run, debug" + id="com.windriver.debug.tcf.LaunchConfigurationType"> + </launchConfigurationType> + </extension> + <extension + point="org.eclipse.debug.core.sourceLocators"> + <sourceLocator + name="TCF Source Lookup Director" + class="com.windriver.debug.tcf.core.launch.TCFSourceLookupDirector" + id="com.windriver.debug.tcf.SourceLocator"> + </sourceLocator> + </extension> + <extension + point="org.eclipse.debug.core.sourcePathComputers"> + <sourcePathComputer + class="com.windriver.debug.tcf.core.launch.TCFSourcePathComputerDelegate" + id="com.windriver.debug.tcf.SourcePathComputer"> + </sourcePathComputer> + </extension> +</plugin> diff --git a/plugins/com.windriver.debug.tcf.core/schema/startup.exsd b/plugins/com.windriver.debug.tcf.core/schema/startup.exsd new file mode 100644 index 000000000..0d41bb8d9 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/schema/startup.exsd @@ -0,0 +1,73 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="com.windriver.debug.tcf"> +<annotation> + <appInfo> + <meta.schema plugin="com.windriver.debug.tcf.core" id="startup" name="TCF Startup"/> + </appInfo> + <documentation> + This extension point is used to register plugins + that want to be activated on TCF startup. + Once the TCF is started, registered plugins will be activated. + </documentation> + +</annotation> + + <element name="extension"> + <complexType> + <sequence> + <element ref="class" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + a fully qualified identifier of the target extension point + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + an optional identifier of the extension instance + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + an optional name of the extension instance + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <element name="class"> + <complexType> + <attribute name="name" type="string"> + <annotation> + <documentation> + Class will be loaded during statup + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + </documentation> + </annotation> + +</schema> diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/TCFCore.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/TCFCore.java new file mode 100644 index 000000000..e4ffe006d --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/TCFCore.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +import com.windriver.debug.tcf.core.model.TCFBreakpointsModel; + +/** + * The activator class controls the plug-in life cycle + */ +public class TCFCore extends Plugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.windriver.debug.tcf.core"; + + // The shared instance + private static TCFCore plugin; + private static TCFBreakpointsModel bp_model; + + public TCFCore() { + plugin = this; + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + bp_model = new TCFBreakpointsModel(); + runTCFStartup(); + } + + @Override + public void stop(BundleContext context) throws Exception { + bp_model.dispose(); + bp_model = null; + plugin = null; + super.stop(context); + } + + private void runTCFStartup() { + try { + IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(PLUGIN_ID, "startup"); + IExtension[] extensions = point.getExtensions(); + for (int i = 0; i < extensions.length; i++) { + try { + Platform.getBundle(extensions[i].getNamespaceIdentifier()).start(); + IConfigurationElement[] e = extensions[i].getConfigurationElements(); + for (int j = 0; j < e.length; j++) { + String nm = e[j].getName(); + if (nm.equals("class")) { //$NON-NLS-1$ + Class.forName(e[j].getAttribute("name")); //$NON-NLS-1$ + } + } + } + catch (Throwable x) { + log("TCF startup error", x); + } + } + } + catch (Exception x) { + log("TCF startup error", x); + } + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static TCFCore getDefault() { + return plugin; + } + + public static TCFBreakpointsModel getBreakpointsModel() { + return bp_model; + } + + /** + * Send error message into Eclipse log. + * @param msg - error message test + * @param err - exception + */ + public static void log(String msg, Throwable err) { + getDefault().getLog().log(new Status(IStatus.ERROR, + getDefault().getBundle().getSymbolicName(), IStatus.OK, msg, err)); + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFLaunchDelegate.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFLaunchDelegate.java new file mode 100644 index 000000000..8a555cfc3 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFLaunchDelegate.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.launch; + +import java.io.IOException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.model.LaunchConfigurationDelegate; + +import com.windriver.debug.tcf.core.model.ITCFConstants; +import com.windriver.debug.tcf.core.model.TCFLaunch; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.Protocol; + +public class TCFLaunchDelegate extends LaunchConfigurationDelegate { + + public static final String + ATTR_PEER_ID = ITCFConstants.ID_TCF_DEBUG_MODEL + ".PeerID", + ATTR_PROGRAM_FILE = ITCFConstants.ID_TCF_DEBUG_MODEL + ".ProgramFile", + ATTR_PROGRAM_ARGUMENTS = ITCFConstants.ID_TCF_DEBUG_MODEL + ".ProgramArguments", + ATTR_WORKING_DIRECTORY = ITCFConstants.ID_TCF_DEBUG_MODEL + ".WorkingDirectory"; + + public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException { + return new TCFLaunch(configuration, mode); + } + + public void launch(final ILaunchConfiguration configuration, final String mode, + final ILaunch launch, IProgressMonitor monitor) throws CoreException { + if (monitor == null) monitor = new NullProgressMonitor(); + monitor.beginTask("Launching debugger session", 1); //$NON-NLS-1$ + Protocol.invokeAndWait(new Runnable() { + public void run() { + try { + String id = configuration.getAttribute(TCFLaunchDelegate.ATTR_PEER_ID, ""); + IPeer peer = Protocol.getLocator().getPeers().get(id); + if (peer == null) throw new IOException("Cannot locate peer " + id); + TCFLaunch.TerminateListener term = null; + if (peer instanceof TCFLaunch.TerminateListener) term = (TCFLaunch.TerminateListener)peer; + ((TCFLaunch)launch).launchTCF(mode, peer, term); + } + catch (Throwable e) { + ((TCFLaunch)launch).setError(e); + } + } + }); + monitor.done(); + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourceLookupDirector.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourceLookupDirector.java new file mode 100644 index 000000000..57f07834f --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourceLookupDirector.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.launch; + +import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector; +import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; + +/** + * TCF source lookup director. For TCF source lookup there is one source lookup + * participant. + */ +public class TCFSourceLookupDirector extends AbstractSourceLookupDirector { + + public void initializeParticipants() { + addParticipants(new ISourceLookupParticipant[] { new TCFSourceLookupParticipant() }); + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourceLookupParticipant.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourceLookupParticipant.java new file mode 100644 index 000000000..765476df1 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourceLookupParticipant.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.launch; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupParticipant; + +/** + * The TCF source lookup participant knows how to translate a TCF stack frame + * into a source file name + */ +public class TCFSourceLookupParticipant extends AbstractSourceLookupParticipant { + + public String getSourceName(Object object) throws CoreException { + return null; + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourcePathComputerDelegate.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourcePathComputerDelegate.java new file mode 100644 index 000000000..f14abaae0 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/launch/TCFSourcePathComputerDelegate.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.launch; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.debug.core.sourcelookup.ISourcePathComputerDelegate; +import org.eclipse.debug.core.sourcelookup.containers.WorkspaceSourceContainer; + +/** + * Computes the default source lookup path for a TCF launch configuration. The + * default source lookup path is the folder or project containing the TCF + * program being launched. If the program is not specified, the workspace is + * searched by default. + */ +public class TCFSourcePathComputerDelegate implements + ISourcePathComputerDelegate { + + public ISourceContainer[] computeSourceContainers( + ILaunchConfiguration configuration, IProgressMonitor monitor) + throws CoreException { + ISourceContainer sourceContainer = null; + /* + * String path = + * configuration.getAttribute(IPDAConstants.ATTR_PDA_PROGRAM, + * (String)null); if (path != null) { IResource resource = + * ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(path)); + * if (resource != null) { IContainer container = resource.getParent(); + * if (container.getType() == IResource.PROJECT) { sourceContainer = new + * ProjectSourceContainer((IProject)container, false); } else if + * (container.getType() == IResource.FOLDER) { sourceContainer = new + * FolderSourceContainer(container, false); } } } + */ + if (sourceContainer == null) { + sourceContainer = new WorkspaceSourceContainer(); + } + return new ISourceContainer[] { sourceContainer }; + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/ITCFBreakpointListener.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/ITCFBreakpointListener.java new file mode 100644 index 000000000..a78f8127f --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/ITCFBreakpointListener.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.model; + +public interface ITCFBreakpointListener { + + public void breakpointStatusChanged(String id); +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/ITCFConstants.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/ITCFConstants.java new file mode 100644 index 000000000..66cba6273 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/ITCFConstants.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.model; + +public interface ITCFConstants { + + /** + * Unique identifier for the TCF debug model + */ + public static final String ID_TCF_DEBUG_MODEL = "com.windriver.debug.tcf"; + +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpoint.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpoint.java new file mode 100644 index 000000000..3b4e912ab --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpoint.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.model; + +import java.math.BigInteger; +import java.util.Map; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.model.Breakpoint; + +import com.windriver.debug.tcf.core.TCFCore; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IBreakpoints; + +public class TCFBreakpoint extends Breakpoint { + + public static final String MARKER_TYPE = "com.windriver.debug.tcf.breakpoint.marker"; + + private static long last_id = 0; + + private static String createNewID() { + assert Protocol.isDispatchThread(); + long id = System.currentTimeMillis(); + if (id <= last_id) id = last_id + 1; + last_id = id; + return Long.toHexString(id); + } + + private String text; + + public TCFBreakpoint() { + } + + public TCFBreakpoint(final IResource resource, Map<String,Object> props) throws DebugException { + props.put(IBreakpoints.PROP_ID, createNewID()); + final Map<String,Object> m = TCFCore.getBreakpointsModel().toMarkerAttributes(props); + final IWorkspaceRunnable runnable = new IWorkspaceRunnable() { + public void run(IProgressMonitor monitor) throws CoreException { + IMarker marker = resource.createMarker(MARKER_TYPE); + setMarker(marker); + marker.setAttributes(m); + DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(TCFBreakpoint.this); + } + }; + final ISchedulingRule rule = getMarkerRule(resource); + Job job = new Job("Add Breakpoint") { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + try { + TCFBreakpoint.this.run(getMarkerRule(resource), runnable); + } + catch (CoreException e) { + return e.getStatus(); + } + return Status.OK_STATUS; + } + }; + job.setRule(rule); + job.schedule(); + } + + public String getModelIdentifier() { + return ITCFConstants.ID_TCF_DEBUG_MODEL; + } + + public String getText() { + if (text == null) { + IMarker marker = getMarker(); + if (marker == null) return null; + StringBuffer bf = new StringBuffer(); + String address = marker.getAttribute( + ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_ADDRESS, null); + if (address != null && address.length() > 0) { + bf.append("PC = "); + BigInteger n = new BigInteger(address, 10); + String s = n.toString(16); + int l = Math.min(s.length(), 8); + bf.append("0x00000000".substring(0, 10 - l)); + bf.append(s); + } + else { + String id = marker.getAttribute( + ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_ID, null); + bf.append("BP"); + bf.append(id); + } + text = bf.toString(); + } + return text; + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpointsModel.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpointsModel.java new file mode 100644 index 000000000..a1998a9e2 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpointsModel.java @@ -0,0 +1,321 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.model; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointListener; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.IBreakpointManagerListener; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IBreakpoint; + +import com.windriver.debug.tcf.core.TCFCore; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IBreakpoints; + +public class TCFBreakpointsModel implements IBreakpointListener, IBreakpointManagerListener { + + private static final String PROP_ID = "ID"; + private final IBreakpointManager bp_manager = DebugPlugin.getDefault().getBreakpointManager(); + + private abstract class BreakpointUpdate implements Runnable { + + private final ILaunch[] launches; + private final Map<String,Object> marker_attrs; + private final String marker_file; + + IBreakpoints service; + IBreakpoints.DoneCommand done; + Map<String,Object> tcf_attrs; + + @SuppressWarnings("unchecked") + BreakpointUpdate(IBreakpoint breakpoint) throws CoreException, IOException { + if (breakpoint == null) { + marker_attrs = null; + marker_file = null; + } + else { + marker_attrs = new HashMap<String,Object>(breakpoint.getMarker().getAttributes()); + marker_file = getFilePath(breakpoint.getMarker().getResource()); + } + launches = DebugPlugin.getDefault().getLaunchManager().getLaunches(); + } + + synchronized void exec() throws InterruptedException { + assert !Protocol.isDispatchThread(); + Protocol.invokeLater(this); + wait(); + } + + public void run() { + if (marker_attrs != null) { + tcf_attrs = toBreakpointAttributes(marker_file, marker_attrs); + } + for (int i = 0; i < launches.length; i++) { + if (launches[i] instanceof TCFLaunch) { + TCFLaunch launch = (TCFLaunch)launches[i]; + final IChannel channel = launch.getChannel(); + if (channel == null) continue; + if (channel.getState() != IChannel.STATE_OPEN) continue; + service = channel.getRemoteService(IBreakpoints.class); + if (service == null) continue; + done = new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) channel.terminate(error); + } + }; + update(); + } + } + Protocol.sync(new Runnable() { + public void run() { + synchronized (BreakpointUpdate.this) { + BreakpointUpdate.this.notify(); + } + } + }); + }; + + abstract void update(); + } + + public TCFBreakpointsModel() { + bp_manager.addBreakpointListener(this); + bp_manager.addBreakpointManagerListener(this); + } + + public void dispose() { + bp_manager.removeBreakpointListener(this); + bp_manager.removeBreakpointManagerListener(this); + } + + @SuppressWarnings("unchecked") + public void downloadBreakpoints(final IChannel channel, final Runnable done) + throws IOException, CoreException { + assert Protocol.isDispatchThread(); + IBreakpoints service = channel.getRemoteService(IBreakpoints.class); + if (service != null) { + IBreakpoint[] arr = bp_manager.getBreakpoints(ITCFConstants.ID_TCF_DEBUG_MODEL); + if (arr != null && arr.length > 0) { + Map<String,Object>[] bps = new Map[arr.length]; + for (int i = 0; i < arr.length; i++) { + IMarker marker = arr[i].getMarker(); + String file = getFilePath(marker.getResource()); + bps[i] = toBreakpointAttributes(file, marker.getAttributes()); + } + service.set(bps, new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error == null) done.run(); + else channel.terminate(error); + } + }); + return; + } + } + Protocol.invokeLater(done); + } + + public void breakpointManagerEnablementChanged(final boolean enabled) { + try { + IBreakpoint[] arr = bp_manager.getBreakpoints(ITCFConstants.ID_TCF_DEBUG_MODEL); + if (arr == null || arr.length == 0) return; + final Set<String> ids = new HashSet<String>(); + for (int i = 0; i < arr.length; i++) { + IMarker marker = arr[i].getMarker(); + Boolean b = marker.getAttribute(IBreakpoint.ENABLED, Boolean.FALSE); + if (!b.booleanValue()) continue; + ids.add(marker.getAttribute( + ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_ID, (String)null)); + } + if (ids.isEmpty()) return; + new BreakpointUpdate(null) { + @Override + void update() { + if (enabled) { + service.enable(ids.toArray(new String[ids.size()]), done); + } + else { + service.disable(ids.toArray(new String[ids.size()]), done); + } + } + }.exec(); + } + catch (Throwable x) { + TCFCore.log("Unhandled exception in breakpoint listener", x); + } + } + + private String getFilePath(IResource resource) throws IOException { + if (resource == ResourcesPlugin.getWorkspace().getRoot()) return null; + IPath p = resource.getRawLocation(); + if (p == null) return null; + return p.toFile().getCanonicalPath(); + } + + public void breakpointAdded(IBreakpoint breakpoint) { + try { + if (!breakpoint.getModelIdentifier().equals(ITCFConstants.ID_TCF_DEBUG_MODEL)) return; + new BreakpointUpdate(breakpoint) { + @Override + void update() { + service.add(tcf_attrs, done); + } + }.exec(); + } + catch (Throwable x) { + TCFCore.log("Unhandled exception in breakpoint listener", x); + } + } + + @SuppressWarnings("unchecked") + private Set<String> calcMarkerDeltaKeys(IMarker marker, IMarkerDelta delta) throws CoreException { + assert delta.getKind() == IResourceDelta.CHANGED; + Map<String,Object> m0 = delta.getAttributes(); + Map<String,Object> m1 = marker.getAttributes(); + Set<String> keys = new HashSet<String>(); + if (m0 != null) keys.addAll(m0.keySet()); + if (m1 != null) keys.addAll(m1.keySet()); + for (Iterator<String> i = keys.iterator(); i.hasNext();) { + String key = i.next(); + Object v0 = m0 != null ? m0.get(key) : null; + Object v1 = m1 != null ? m1.get(key) : null; + if (v0 instanceof String && ((String)v0).length() == 0) v0 = null; + if (v1 instanceof String && ((String)v1).length() == 0) v1 = null; + if (v0 instanceof Boolean && !((Boolean)v0).booleanValue()) v0 = null; + if (v1 instanceof Boolean && !((Boolean)v1).booleanValue()) v1 = null; + if ((v0 == null) != (v1 == null)) continue; + if (v0 != null && !v0.equals(v1)) continue; + i.remove(); + } + return keys; + } + + public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) { + try { + if (!breakpoint.getModelIdentifier().equals(ITCFConstants.ID_TCF_DEBUG_MODEL)) return; + final Set<String> s = calcMarkerDeltaKeys(breakpoint.getMarker(), delta); + if (s.isEmpty()) return; + new BreakpointUpdate(breakpoint) { + @Override + void update() { + if (s.size() == 1 && s.contains(IBreakpoint.ENABLED)) { + Boolean enabled = (Boolean)tcf_attrs.get(IBreakpoints.PROP_ENABLED); + if (enabled == null || !enabled.booleanValue()) { + service.disable(new String[]{ (String)tcf_attrs.get(PROP_ID) }, done); + } + else { + service.enable(new String[]{ (String)tcf_attrs.get(PROP_ID) }, done); + } + } + else { + service.change(tcf_attrs, done); + } + } + }.exec(); + } + catch (Throwable x) { + TCFCore.log("Unhandled exception in breakpoint listener", x); + } + } + + public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) { + try { + if (!breakpoint.getModelIdentifier().equals(ITCFConstants.ID_TCF_DEBUG_MODEL)) return; + new BreakpointUpdate(breakpoint) { + @Override + void update() { + service.remove(new String[]{ (String)tcf_attrs.get(PROP_ID) }, done); + } + }.exec(); + } + catch (Throwable x) { + TCFCore.log("Unhandled exception in breakpoint listener", x); + } + } + + public Map<String,Object> toMarkerAttributes(Map<String,Object> p) { + assert Protocol.isDispatchThread(); + Map<String,Object> m = new HashMap<String,Object>(); + for (Iterator<Map.Entry<String,Object>> i = p.entrySet().iterator(); i.hasNext();) { + Map.Entry<String,Object> e = i.next(); + String key = e.getKey(); + Object val = e.getValue(); + if (key.equals(IBreakpoints.PROP_ENABLED)) continue; + if (key.equals(IBreakpoints.PROP_FILE)) continue; + if (key.equals(IBreakpoints.PROP_LINE)) continue; + if (key.equals(IBreakpoints.PROP_COLUMN)) continue; + m.put(ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + key, val); + } + Boolean enabled = (Boolean)p.get(IBreakpoints.PROP_ENABLED); + if (enabled == null) m.put(IBreakpoint.ENABLED, Boolean.FALSE); + else m.put(IBreakpoint.ENABLED, enabled); + m.put(IBreakpoint.REGISTERED, Boolean.TRUE); + m.put(IBreakpoint.PERSISTED, Boolean.TRUE); + m.put(IBreakpoint.ID, ITCFConstants.ID_TCF_DEBUG_MODEL); + String msg = ""; + if (p.get(IBreakpoints.PROP_ADDRESS) != null) msg += p.get(IBreakpoints.PROP_ADDRESS); + m.put(IMarker.MESSAGE, "Breakpoint: " + msg); + Number line = (Number)p.get(IBreakpoints.PROP_LINE); + if (line != null) { + m.put(IMarker.LINE_NUMBER, Integer.toString(line.intValue() + 1)); + Number column = (Number)p.get(IBreakpoints.PROP_COLUMN); + if (column != null) { + m.put(IMarker.CHAR_START, column.toString()); + m.put(IMarker.CHAR_END, Integer.toString(column.intValue() + 1)); + } + } + return m; + } + + public Map<String,Object> toBreakpointAttributes(String file, Map<String,Object> p) { + assert Protocol.isDispatchThread(); + Map<String,Object> m = new HashMap<String,Object>(); + for (Iterator<Map.Entry<String,Object>> i = p.entrySet().iterator(); i.hasNext();) { + Map.Entry<String,Object> e = i.next(); + String key = e.getKey(); + Object val = e.getValue(); + if (!key.startsWith(ITCFConstants.ID_TCF_DEBUG_MODEL)) continue; + m.put(key.substring(ITCFConstants.ID_TCF_DEBUG_MODEL.length() + 1), val); + } + Boolean enabled = (Boolean)p.get(IBreakpoint.ENABLED); + if (enabled != null && enabled.booleanValue() && bp_manager.isEnabled()) { + m.put(IBreakpoints.PROP_ENABLED, enabled); + } + if (file != null) { + m.put(IBreakpoints.PROP_FILE, file); + String line = (String)p.get(IMarker.LINE_NUMBER); + if (line != null) { + m.put(IBreakpoints.PROP_LINE, new Integer(Integer.parseInt(line) - 1)); + String column = (String)p.get(IMarker.CHAR_START); + if (column != null) { + m.put(IBreakpoints.PROP_COLUMN, new Integer(column)); + } + } + } + return m; + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpointsStatus.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpointsStatus.java new file mode 100644 index 000000000..a9a6cb735 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFBreakpointsStatus.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.model; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.debug.core.model.IBreakpoint; + +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IBreakpoints; + +public class TCFBreakpointsStatus { + + private final IBreakpoints service; + private final Map<String,Map<String,Object>> status = new HashMap<String,Map<String,Object>>(); + private final Set<ITCFBreakpointListener> listeners = new HashSet<ITCFBreakpointListener>(); + + private static final Map<String,Object> status_not_supported = new HashMap<String,Object>(); + + static { + status_not_supported.put(IBreakpoints.STATUS_ERROR, "Not supported"); + } + + TCFBreakpointsStatus(TCFLaunch launch) { + assert Protocol.isDispatchThread(); + service = launch.getChannel().getRemoteService(IBreakpoints.class); + if (service != null) { + service.addListener(new IBreakpoints.BreakpointsListener() { + + public void breakpointStatusChanged(String id, Map<String, Object> status) { + assert Protocol.isDispatchThread(); + TCFBreakpointsStatus.this.status.put(id, status); + for (Iterator<ITCFBreakpointListener> i = listeners.iterator(); i.hasNext();) { + i.next().breakpointStatusChanged(id); + } + } + }); + } + } + + public Map<String, Object> getStatus(String id) { + assert id != null; + assert Protocol.isDispatchThread(); + if (service == null) return status_not_supported; + return status.get(id); + } + + public Map<String, Object> getStatus(IBreakpoint bp) { + if (!bp.getModelIdentifier().equals(ITCFConstants.ID_TCF_DEBUG_MODEL)) return status_not_supported; + IMarker marker = bp.getMarker(); + if (marker == null) return null; + return getStatus(marker.getAttribute( + ITCFConstants.ID_TCF_DEBUG_MODEL + '.' + IBreakpoints.PROP_ID, null)); + } + + public void addListener(ITCFBreakpointListener listener) { + assert Protocol.isDispatchThread(); + listeners.add(listener); + } + + public void removeListener(ITCFBreakpointListener listener) { + assert Protocol.isDispatchThread(); + listeners.remove(listener); + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFError.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFError.java new file mode 100644 index 000000000..f754eb7c2 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFError.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.model; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.debug.core.DebugException; + +import com.windriver.debug.tcf.core.TCFCore; + +public class TCFError extends DebugException { + + private static final long serialVersionUID = -4261097789666829020L; + + public TCFError(Throwable exception) { + super(new Status(exception)); + } + + private static class Status implements IStatus { + + private final Throwable exception; + + private Status(Throwable exception) { + this.exception = exception; + } + + public IStatus[] getChildren() { + return null; + } + + public int getCode() { + return 1; + } + + public Throwable getException() { + return exception; + } + + public String getMessage() { + return exception.getMessage(); + } + + public String getPlugin() { + return TCFCore.PLUGIN_ID; + } + + public int getSeverity() { + return ERROR; + } + + public boolean isMultiStatus() { + return false; + } + + public boolean isOK() { + return false; + } + + public boolean matches(int severityMask) { + return false; + } + } +} diff --git a/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFLaunch.java b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFLaunch.java new file mode 100644 index 000000000..a7a07eda7 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.core/src/com/windriver/debug/tcf/core/model/TCFLaunch.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.core.model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.core.Launch; + +import com.windriver.debug.tcf.core.TCFCore; +import com.windriver.debug.tcf.core.launch.TCFLaunchDelegate; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IProcesses; +import com.windriver.tcf.api.services.IProcesses.ProcessContext; + +public class TCFLaunch extends Launch { + + public interface Listener { + + public void onConnected(TCFLaunch launch); + + public void onDisconnected(TCFLaunch launch); + + } + + public interface TerminateListener { + + public boolean canTerminate(); + + public boolean isTerminated(); + + public void terminate(Runnable done); + } + + private static final Collection<Listener> listeners = new ArrayList<Listener>(); + + private IChannel channel; + private Throwable error; + private TerminateListener terminate_listener; + private TCFBreakpointsStatus breakpoints_status; + private String mode; + private boolean connecting; + private ProcessContext process; + + public TCFLaunch(ILaunchConfiguration launchConfiguration, String mode) { + super(launchConfiguration, mode, null); + } + + private void onConnected() { + try { + final Runnable done = new Runnable() { + public void run() { + connecting = false; + for (Listener l : listeners) l.onConnected(TCFLaunch.this); + fireChanged(); + } + }; + if (mode.equals(ILaunchManager.DEBUG_MODE)) { + TCFCore.getBreakpointsModel().downloadBreakpoints(channel, new Runnable() { + public void run() { + if (channel.getState() != IChannel.STATE_OPEN) return; + breakpoints_status = new TCFBreakpointsStatus(TCFLaunch.this); + downloadApplication(done); + } + }); + } + else { + downloadApplication(done); + } + } + catch (Exception x) { + channel.terminate(x); + } + } + + @SuppressWarnings("unchecked") + protected void downloadApplication(final Runnable done) { + try { + ILaunchConfiguration cfg = getLaunchConfiguration(); + final String file = cfg.getAttribute(TCFLaunchDelegate.ATTR_PROGRAM_FILE, ""); + if (file.length() == 0) { + Protocol.invokeLater(done); + return; + } + final String dir = cfg.getAttribute(TCFLaunchDelegate.ATTR_WORKING_DIRECTORY, ""); + final String args = cfg.getAttribute(TCFLaunchDelegate.ATTR_PROGRAM_ARGUMENTS, ""); + final Map<String,String> env = cfg.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map)null); + final boolean append = cfg.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); + final boolean attach = mode.equals(ILaunchManager.DEBUG_MODE); + final IProcesses ps = channel.getRemoteService(IProcesses.class); + if (ps == null) throw new Exception("Target does not provide Processes service"); + IProcesses.DoneGetEnvironment done_env = new IProcesses.DoneGetEnvironment() { + public void doneGetEnvironment(IToken token, Exception error, Map<String,String> def) { + if (error != null) { + channel.terminate(error); + return; + } + Map<String,String> vars = new HashMap<String,String>(); + if (append) vars.putAll(def); + if (env != null) vars.putAll(env); + ps.start(dir, file, toArgsArray(args), vars, attach, new IProcesses.DoneStart() { + public void doneStart(IToken token, Exception error, ProcessContext process) { + if (error != null) { + channel.terminate(error); + return; + } + TCFLaunch.this.process = process; + Protocol.invokeLater(done); + } + }); + } + }; + if (append) ps.getEnvironment(done_env); + else done_env.doneGetEnvironment(null, null, null); + } + catch (Exception x) { + channel.terminate(x); + } + } + + private String[] toArgsArray(String cmd) { + int i = 0; + int l = cmd.length(); + List<String> arr = new ArrayList<String>(); + for (;;) { + while (i < l && cmd.charAt(i) == ' ') i++; + if (i >= l) break; + String s = null; + if (cmd.charAt(i) == '"') { + i++; + StringBuffer bf = new StringBuffer(); + while (i < l) { + char ch = cmd.charAt(i++); + if (ch == '"') break; + if (ch == '\\' && i < l) ch = cmd.charAt(i++); + bf.append(ch); + } + s = bf.toString(); + } + else { + int i0 = i; + while (i < l && cmd.charAt(i) != ' ') i++; + s = cmd.substring(i0, i); + } + arr.add(s); + } + return arr.toArray(new String[arr.size()]); + } + + private void onDisconnected(Throwable error) { + this.error = error; + breakpoints_status = null; + connecting = false; + for (Iterator<Listener> i = listeners.iterator(); i.hasNext();) { + i.next().onDisconnected(this); + } + if (DebugPlugin.getDefault() != null) fireTerminate(); + } + + /*--------------------------------------------------------------------------------------------*/ + + public Throwable getError() { + return error; + } + + public void setError(Throwable x) { + if (error != null) return; + error = x; + if (channel != null && channel.getState() == IChannel.STATE_OPEN) { + channel.terminate(x); + } + fireChanged(); + } + + public TCFBreakpointsStatus getBreakpointsStatus() { + return breakpoints_status; + } + + public static void addListener(Listener listener) { + assert Protocol.isDispatchThread(); + listeners.add(listener); + } + + public static void removeListener(Listener listener) { + assert Protocol.isDispatchThread(); + listeners.remove(listener); + } + + public IChannel getChannel() { + assert Protocol.isDispatchThread(); + if (channel == null || channel.getState() != IChannel.STATE_OPEN) return null; + return channel; + } + + public IProcesses.ProcessContext getProcessContext() { + return process; + } + + public boolean isConnecting() { + return connecting; + } + + public IPeer getPeer() { + assert Protocol.isDispatchThread(); + return channel.getRemotePeer(); + } + + public <V extends IService> V getService(Class<V> cls) { + assert Protocol.isDispatchThread(); + return channel.getRemoteService(cls); + } + + public boolean canTerminate() { + final boolean res[] = new boolean[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + if (terminate_listener == null) res[0] = false; + else res[0] = terminate_listener.canTerminate(); + } + }); + return res[0]; + } + + public boolean isTerminated() { + final boolean res[] = new boolean[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + if (channel == null || channel.getState() == IChannel.STATE_CLOSED) res[0] = true; + else if (terminate_listener == null) res[0] = false; + else res[0] = terminate_listener.isTerminated(); + } + }); + return res[0]; + } + + public void terminate() { + Protocol.invokeAndWait(new Runnable() { + public void run() { + if (terminate_listener == null) return; + terminate_listener.terminate(new Runnable() { + public void run() { + fireTerminate(); + } + }); + } + }); + } + + public void terminate(Runnable done) { + if (terminate_listener == null) done.run(); + else terminate_listener.terminate(done); + } + + public boolean canDisconnect() { + final boolean res[] = new boolean[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + res[0] = channel != null && channel.getState() != IChannel.STATE_CLOSED; + } + }); + return res[0]; + } + + public boolean isDisconnected() { + final boolean res[] = new boolean[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + res[0] = channel == null || channel.getState() == IChannel.STATE_CLOSED; + } + }); + return res[0]; + } + + public void disconnect() throws DebugException { + Protocol.invokeAndWait(new Runnable() { + public void run() { + if (channel != null && channel.getState() != IChannel.STATE_CLOSED) { + channel.close(); + } + fireTerminate(); + } + }); + } + + public void launchTCF(String mode, IPeer peer, TerminateListener terminate_listener) throws DebugException { + assert Protocol.isDispatchThread(); + this.mode = mode; + this.terminate_listener = terminate_listener; + connecting = true; + channel = peer.openChannel(); + channel.addChannelListener(new IChannel.IChannelListener() { + + public void onChannelOpened() { + onConnected(); + } + + public void congestionLevel(int level) { + } + + public void onChannelClosed(Throwable error) { + channel.removeChannelListener(this); + onDisconnected(error); + } + + }); + assert channel.getState() == IChannel.STATE_OPENNING; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/.classpath b/plugins/com.windriver.debug.tcf.ui/.classpath new file mode 100644 index 000000000..03ba07a5f --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/.classpath @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"> + <accessrules> + <accessrule kind="accessible" pattern="org/eclipse/debug/**/provisional/**"/> + </accessrules> + </classpathentry> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/com.windriver.debug.tcf.ui/.cvsignore b/plugins/com.windriver.debug.tcf.ui/.cvsignore new file mode 100644 index 000000000..ba077a403 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/plugins/com.windriver.debug.tcf.ui/.project b/plugins/com.windriver.debug.tcf.ui/.project new file mode 100644 index 000000000..fb751e4e7 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.windriver.debug.tcf.ui</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/plugins/com.windriver.debug.tcf.ui/.settings/org.eclipse.jdt.core.prefs b/plugins/com.windriver.debug.tcf.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..78e20524b --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +#Mon Sep 10 12:26:30 PDT 2007 +eclipse.preferences.version=1 +org.eclipse.jdt.core.builder.cleanOutputFolder=clean +org.eclipse.jdt.core.builder.duplicateResourceTask=warning +org.eclipse.jdt.core.builder.invalidClasspath=abort +org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore +org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,.svn/ +org.eclipse.jdt.core.circularClasspath=error +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 +org.eclipse.jdt.core.incompatibleJDKLevel=ignore +org.eclipse.jdt.core.incompleteClasspath=error diff --git a/plugins/com.windriver.debug.tcf.ui/META-INF/MANIFEST.MF b/plugins/com.windriver.debug.tcf.ui/META-INF/MANIFEST.MF new file mode 100644 index 000000000..4743cd6da --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: com.windriver.debug.tcf.ui;singleton:=true +Bundle-Version: 0.1.0 +Bundle-Activator: com.windriver.debug.tcf.ui.TCFUI +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.debug.core, + org.eclipse.debug.ui, + com.windriver.debug.tcf.core, + com.windriver.tcf.api +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Eclipse-LazyStart: true +Export-Package: com.windriver.debug.tcf.ui.adapters, + com.windriver.debug.tcf.ui.launch diff --git a/plugins/com.windriver.debug.tcf.ui/about.html b/plugins/com.windriver.debug.tcf.ui/about.html new file mode 100755 index 000000000..6c5b3615b --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>January 10, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/plugins/com.windriver.debug.tcf.ui/build.properties b/plugins/com.windriver.debug.tcf.ui/build.properties new file mode 100644 index 000000000..bbc49de86 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/build.properties @@ -0,0 +1,7 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + bin/ + diff --git a/plugins/com.windriver.debug.tcf.ui/icons/arguments_tab.gif b/plugins/com.windriver.debug.tcf.ui/icons/arguments_tab.gif Binary files differnew file mode 100644 index 000000000..44660b5f0 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/icons/arguments_tab.gif diff --git a/plugins/com.windriver.debug.tcf.ui/icons/tcf.gif b/plugins/com.windriver.debug.tcf.ui/icons/tcf.gif Binary files differnew file mode 100644 index 000000000..3198679ae --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/icons/tcf.gif diff --git a/plugins/com.windriver.debug.tcf.ui/plugin.properties b/plugins/com.windriver.debug.tcf.ui/plugin.properties new file mode 100644 index 000000000..c791ca4f7 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2007 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Wind River Systems - initial implementation +############################################################################### +pluginName = TCF/Eclipse Debugger Integration UI +providerName = Eclipse.org + diff --git a/plugins/com.windriver.debug.tcf.ui/plugin.xml b/plugins/com.windriver.debug.tcf.ui/plugin.xml new file mode 100644 index 000000000..cc3b88d16 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/plugin.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + + <extension point="com.windriver.debug.tcf.core.startup"/> + + <extension + id="com.windriver.debug.tcf.ui.adapters" + point="org.eclipse.core.runtime.adapters"> + <factory + class="com.windriver.debug.tcf.ui.adapters.TCFLaunchAdapterFactory" + adaptableType="com.windriver.debug.tcf.core.model.TCFLaunch"> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider"/> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider"/> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory"/> + <adapter type="org.eclipse.debug.core.commands.ITerminateHandler"/> + </factory> + <factory + class="com.windriver.debug.tcf.ui.adapters.TCFBreakpointAdapterFactory" + adaptableType="com.windriver.debug.tcf.ui.model.TCFNode"> + <adapter type="org.eclipse.debug.ui.actions.IToggleBreakpointsTarget"/> + <adapter type="org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension"/> + </factory> + </extension> + + <extension + point="org.eclipse.debug.ui.debugModelPresentations"> + <debugModelPresentation + class = "com.windriver.debug.tcf.ui.model.TCFModelPresentation" + id = "com.windriver.debug.tcf"> + </debugModelPresentation> + </extension> + + <extension + point="org.eclipse.debug.ui.launchConfigurationTypeImages"> + <launchConfigurationTypeImage + icon="icons/tcf.gif" + configTypeID="com.windriver.debug.tcf.LaunchConfigurationType" + id="com.windriver.debug.tcf.LaunchConfigurationTypeImage"> + </launchConfigurationTypeImage> + </extension> + + <extension + point="org.eclipse.debug.ui.launchConfigurationTabGroups"> + <launchConfigurationTabGroup + type="com.windriver.debug.tcf.LaunchConfigurationType" + description="Run or debug a program using Target Communication Framework" + class="com.windriver.debug.tcf.ui.launch.TCFTabGroup" + id="com.windriver.debug.tcf.LaunchConfigurationTabGroup"> + </launchConfigurationTabGroup> + </extension> + + <extension + point="org.eclipse.ui.contexts"> + <context + name="Debugging using Target Communication Framework" + description="Debugging using Target Communication Framework" + id="com.windriver.debug.tcf.ui.debugging" + parentId="org.eclipse.debug.ui.debugging"> + </context> + </extension> + + <extension + point="org.eclipse.ui.views"> + <view + name="TCF Trace" + icon="icons/tcf.gif" + category="org.eclipse.debug.ui" + class="com.windriver.debug.tcf.ui.trace.TraceView" + id="com.windriver.tcf.TraceView"> + </view> + </extension> + + <extension + point="org.eclipse.ui.perspectiveExtensions"> + <perspectiveExtension + targetID="org.eclipse.debug.ui.DebugPerspective"> + <view + relative="org.eclipse.ui.console.ConsoleView" + relationship="stack" + id="com.windriver.tcf.TraceView"> + </view> + </perspectiveExtension> + </extension> + +</plugin> diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/TCFUI.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/TCFUI.java new file mode 100644 index 000000000..5ce3dbb2b --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/TCFUI.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; + +import com.windriver.debug.tcf.ui.model.TCFModelManager; + +/** + * The activator class controls the plug-in life cycle + */ +public class TCFUI extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.windriver.debug.tcf.ui"; + + // The shared instance + private static TCFUI plugin; + private static TCFModelManager model_manager; + + private static final BundleListener bundle_listener = new BundleListener() { + public void bundleChanged(BundleEvent event) { + if (plugin != null && event.getBundle() == plugin.getBundle() && + plugin.getBundle().getState() != Bundle.ACTIVE && model_manager != null) { + model_manager.dispose(); + model_manager = null; + } + } + }; + + /** + * The constructor + */ + public TCFUI() { + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + context.addBundleListener(bundle_listener); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static TCFUI getDefault() { + return plugin; + } + + /** + * Returns the shared TCFModel instance + * + * @return the shared TCFModel instance + */ + public static TCFModelManager getModelManager() { + if (plugin != null && model_manager == null && plugin.getBundle().getState() == Bundle.ACTIVE) { + model_manager = new TCFModelManager(); + } + return model_manager; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFBreakpointAdapterFactory.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFBreakpointAdapterFactory.java new file mode 100644 index 000000000..c5f428dac --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFBreakpointAdapterFactory.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.adapters; + +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension; + +import com.windriver.debug.tcf.ui.commands.BreakpointCommand; +import com.windriver.debug.tcf.ui.model.TCFNode; + +public class TCFBreakpointAdapterFactory implements IAdapterFactory { + + @SuppressWarnings("unchecked") + public Object getAdapter(Object obj, Class adapterType) { + if (obj instanceof TCFNode) { + return new BreakpointCommand(); + } + System.out.println(obj.getClass().getName() + " -> " + adapterType); + return null; + } + + @SuppressWarnings("unchecked") + public Class[] getAdapterList() { + return new Class[]{ IToggleBreakpointsTarget.class, IToggleBreakpointsTargetExtension.class }; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFLaunchAdapterFactory.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFLaunchAdapterFactory.java new file mode 100644 index 000000000..4159fcea4 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFLaunchAdapterFactory.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.adapters; + +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.debug.core.commands.ITerminateHandler; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; + +import com.windriver.debug.tcf.core.model.TCFLaunch; +import com.windriver.debug.tcf.ui.TCFUI; +import com.windriver.debug.tcf.ui.model.TCFModel; +import com.windriver.tcf.api.protocol.Protocol; + +public class TCFLaunchAdapterFactory implements IAdapterFactory { + + @SuppressWarnings("unchecked") + private final Class[] adapter_list = { + IElementContentProvider.class, + IElementLabelProvider.class, + IModelProxyFactory.class, + ITerminateHandler.class + }; + + @SuppressWarnings("unchecked") + public Object getAdapter(final Object from, final Class to) { + if (from instanceof TCFLaunch) { + final Object[] res = new Object[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + TCFLaunch launch = (TCFLaunch)from; + TCFModel model = TCFUI.getModelManager().getModel(launch); + if (model != null) { + if (to.isInstance(model)) { + res[0] = model; + return; + } + Object cmd = model.getCommand(to); + if (cmd != null) { + res[0] = cmd; + return; + } + } + } + }); + if (res[0] != null) return res[0]; + } + System.err.println(from.getClass().getName() + " -> " + to); + return null; + } + + @SuppressWarnings("unchecked") + public Class[] getAdapterList() { + return adapter_list; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFModelSelectionPolicyFactoryAdapter.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFModelSelectionPolicyFactoryAdapter.java new file mode 100644 index 000000000..d8ad27b10 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/adapters/TCFModelSelectionPolicyFactoryAdapter.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.adapters; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +public class TCFModelSelectionPolicyFactoryAdapter implements IModelSelectionPolicyFactory { + + public IModelSelectionPolicy createModelSelectionPolicyAdapter( + Object element, IPresentationContext context) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/BreakpointCommand.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/BreakpointCommand.java new file mode 100644 index 000000000..12ec02760 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/BreakpointCommand.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.commands; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IWorkbenchPart; + +import com.windriver.debug.tcf.core.model.TCFBreakpoint; +import com.windriver.debug.tcf.ui.model.TCFNode; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IBreakpoints; + +public class BreakpointCommand implements IToggleBreakpointsTargetExtension { + + public boolean canToggleBreakpoints(IWorkbenchPart part, ISelection selection) { + Object obj = ((IStructuredSelection)selection).getFirstElement(); + if (obj instanceof TCFNode) { + final TCFNode node = (TCFNode)obj; + if (node == null) return false; + final boolean[] res = new boolean[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + res[0] = node.getAddress() != null; + } + }); + return res[0]; + } + else { + return false; + } + } + + public void toggleBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + Object obj = ((IStructuredSelection)selection).getFirstElement(); + if (obj instanceof TCFNode) { + final TCFNode node = (TCFNode)obj; + if (node == null) return; + final CoreException[] res = new CoreException[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + try { + String addr = node.getAddress(); + if (addr == null) return; + Map<String,Object> m = new HashMap<String,Object>(); + m.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); + m.put(IBreakpoints.PROP_ADDRESS, addr); + new TCFBreakpoint(ResourcesPlugin.getWorkspace().getRoot(), m); + } + catch (CoreException x) { + res[0] = x; + } + } + }); + if (res[0] != null) throw res[0]; + } + } + + public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) { + // TODO Auto-generated method stub + return false; + } + + public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + // TODO Auto-generated method stub + + } + + public boolean canToggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) { + // TODO Auto-generated method stub + return false; + } + + public void toggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + // TODO Auto-generated method stub + + } + + public boolean canToggleWatchpoints(IWorkbenchPart part, ISelection selection) { + // TODO Auto-generated method stub + return false; + } + + public void toggleWatchpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + // TODO Auto-generated method stub + + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/DisconnectCommand.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/DisconnectCommand.java new file mode 100644 index 000000000..3ef011689 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/DisconnectCommand.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.commands; + +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IDisconnectHandler; +import org.eclipse.debug.core.commands.IEnabledStateRequest; + +import com.windriver.debug.tcf.ui.model.TCFModel; +import com.windriver.debug.tcf.ui.model.TCFRunnable; + +public class DisconnectCommand implements IDisconnectHandler { + + private final TCFModel model; + + public DisconnectCommand(TCFModel model) { + this.model = model; + } + + public void canExecute(final IEnabledStateRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + monitor.setEnabled(model.getLaunch().canDisconnect()); + monitor.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + public boolean execute(final IDebugCommandRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + try { + model.getLaunch().disconnect(); + monitor.setStatus(Status.OK_STATUS); + } + catch (DebugException x) { + monitor.setStatus(x.getStatus()); + } + done(); + } + }; + return false; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/ResumeCommand.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/ResumeCommand.java new file mode 100644 index 000000000..f4ecd5049 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/ResumeCommand.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.commands; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IResumeHandler; + +import com.windriver.debug.tcf.ui.TCFUI; +import com.windriver.debug.tcf.ui.model.TCFModel; +import com.windriver.debug.tcf.ui.model.TCFNode; +import com.windriver.debug.tcf.ui.model.TCFRunnable; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IRunControl; + +public class ResumeCommand implements IResumeHandler { + + private final TCFModel model; + + public ResumeCommand(TCFModel model) { + this.model = model; + } + + public void canExecute(final IEnabledStateRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + boolean res = false; + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null) { + node = node.getParent(); + } + else if (ctx.isContainer()) { + if (ctx.canResume(IRunControl.RM_RESUME)) res = true; + node = null; + } + else { + if (node.isSuspended() && ctx.canResume(IRunControl.RM_RESUME)) res = true; + node = null; + } + } + } + monitor.setEnabled(res); + monitor.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + public boolean execute(final IDebugCommandRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null) { + node = node.getParent(); + } + else { + set.add(ctx); + node = null; + } + } + } + final Set<IToken> cmds = new HashSet<IToken>(); + for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { + IRunControl.RunControlContext ctx = i.next(); + cmds.add(ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + assert cmds.contains(token); + cmds.remove(token); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + TCFUI.PLUGIN_ID, IStatus.OK, "Cannot resume", error)); + } + if (cmds.isEmpty()) done(); + } + })); + } + } + }; + return true; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepIntoCommand.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepIntoCommand.java new file mode 100644 index 000000000..76e09d274 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepIntoCommand.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.commands; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IStepIntoHandler; + +import com.windriver.debug.tcf.ui.TCFUI; +import com.windriver.debug.tcf.ui.model.TCFModel; +import com.windriver.debug.tcf.ui.model.TCFNode; +import com.windriver.debug.tcf.ui.model.TCFRunnable; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IRunControl; + +public class StepIntoCommand implements IStepIntoHandler { + + private final TCFModel model; + + public StepIntoCommand(TCFModel model) { + this.model = model; + } + + public void canExecute(final IEnabledStateRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + boolean res = false; + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_INTO)) { + node = node.getParent(); + } + else { + if (node.isSuspended()) res = true; + node = null; + } + } + } + monitor.setEnabled(res); + monitor.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + public boolean execute(final IDebugCommandRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_INTO)) { + node = node.getParent(); + } + else { + set.add(ctx); + node = null; + } + } + } + final Set<IToken> cmds = new HashSet<IToken>(); + for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { + IRunControl.RunControlContext ctx = i.next(); + cmds.add(ctx.resume(IRunControl.RM_STEP_INTO, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + assert cmds.contains(token); + cmds.remove(token); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + TCFUI.PLUGIN_ID, IStatus.OK, "Cannot step into", error)); + } + if (cmds.isEmpty()) done(); + } + })); + } + } + }; + return true; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepOverCommand.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepOverCommand.java new file mode 100644 index 000000000..3a8170fbb --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepOverCommand.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.commands; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IStepOverHandler; + +import com.windriver.debug.tcf.ui.TCFUI; +import com.windriver.debug.tcf.ui.model.TCFModel; +import com.windriver.debug.tcf.ui.model.TCFNode; +import com.windriver.debug.tcf.ui.model.TCFRunnable; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IRunControl; + +public class StepOverCommand implements IStepOverHandler { + + private final TCFModel model; + + public StepOverCommand(TCFModel model) { + this.model = model; + } + + public void canExecute(final IEnabledStateRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + boolean res = false; + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_OVER)) { + node = node.getParent(); + } + else { + if (node.isSuspended()) res = true; + node = null; + } + } + } + monitor.setEnabled(res); + monitor.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + public boolean execute(final IDebugCommandRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_OVER)) { + node = node.getParent(); + } + else { + set.add(ctx); + node = null; + } + } + } + final Set<IToken> cmds = new HashSet<IToken>(); + for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { + IRunControl.RunControlContext ctx = i.next(); + cmds.add(ctx.resume(IRunControl.RM_STEP_OVER, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + assert cmds.contains(token); + cmds.remove(token); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + TCFUI.PLUGIN_ID, IStatus.OK, "Cannot step into", error)); + } + if (cmds.isEmpty()) done(); + } + })); + } + } + }; + return true; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepReturnCommand.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepReturnCommand.java new file mode 100644 index 000000000..8975a8155 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/StepReturnCommand.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.commands; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.IStepReturnHandler; + +import com.windriver.debug.tcf.ui.TCFUI; +import com.windriver.debug.tcf.ui.model.TCFModel; +import com.windriver.debug.tcf.ui.model.TCFNode; +import com.windriver.debug.tcf.ui.model.TCFRunnable; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IRunControl; + +public class StepReturnCommand implements IStepReturnHandler { + + private final TCFModel model; + + public StepReturnCommand(TCFModel model) { + this.model = model; + } + + public void canExecute(final IEnabledStateRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + boolean res = false; + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_OUT)) { + node = node.getParent(); + } + else { + if (node.isSuspended()) res = true; + node = null; + } + } + } + monitor.setEnabled(res); + monitor.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + public boolean execute(final IDebugCommandRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null || !ctx.canResume(IRunControl.RM_STEP_OUT)) { + node = node.getParent(); + } + else { + set.add(ctx); + node = null; + } + } + } + final Set<IToken> cmds = new HashSet<IToken>(); + for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { + IRunControl.RunControlContext ctx = i.next(); + cmds.add(ctx.resume(IRunControl.RM_STEP_OUT, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + assert cmds.contains(token); + cmds.remove(token); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + TCFUI.PLUGIN_ID, IStatus.OK, "Cannot step into", error)); + } + if (cmds.isEmpty()) done(); + } + })); + } + } + }; + return true; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/SuspendCommand.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/SuspendCommand.java new file mode 100644 index 000000000..45419650e --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/SuspendCommand.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.commands; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.ISuspendHandler; + +import com.windriver.debug.tcf.ui.TCFUI; +import com.windriver.debug.tcf.ui.model.TCFModel; +import com.windriver.debug.tcf.ui.model.TCFNode; +import com.windriver.debug.tcf.ui.model.TCFRunnable; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IRunControl; + +public class SuspendCommand implements ISuspendHandler { + + private final TCFModel model; + + public SuspendCommand(TCFModel model) { + this.model = model; + } + + public void canExecute(final IEnabledStateRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + boolean res = false; + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null) { + node = node.getParent(); + } + else if (ctx.isContainer()) { + if (ctx.canSuspend()) res = true; + node = null; + } + else { + if (node.isRunning() && ctx.canSuspend()) res = true; + node = null; + } + } + } + monitor.setEnabled(res); + monitor.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + public boolean execute(final IDebugCommandRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx == null) { + node = node.getParent(); + } + else { + set.add(ctx); + node = null; + } + } + } + final Set<IToken> cmds = new HashSet<IToken>(); + for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { + IRunControl.RunControlContext ctx = i.next(); + cmds.add(ctx.suspend(new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + assert cmds.contains(token); + cmds.remove(token); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + TCFUI.PLUGIN_ID, IStatus.OK, "Cannot suspend", error)); + } + if (cmds.isEmpty()) done(); + } + })); + } + } + }; + return true; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/TerminateCommand.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/TerminateCommand.java new file mode 100644 index 000000000..81f16e12a --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/commands/TerminateCommand.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.commands; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.ITerminateHandler; + +import com.windriver.debug.tcf.ui.TCFUI; +import com.windriver.debug.tcf.ui.model.TCFModel; +import com.windriver.debug.tcf.ui.model.TCFNode; +import com.windriver.debug.tcf.ui.model.TCFRunnable; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IRunControl; + +public class TerminateCommand implements ITerminateHandler { + + private final TCFModel model; + + public TerminateCommand(TCFModel model) { + this.model = model; + } + + public void canExecute(final IEnabledStateRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + boolean res = false; + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx != null && ctx.canTerminate()) { + res = true; + node = null; + } + else { + node = node.getParent(); + if (node == null && model.getLaunch().canTerminate()) res = true; + } + } + } + monitor.setEnabled(res); + monitor.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + public boolean execute(final IDebugCommandRequest monitor) { + new TCFRunnable(model.getDisplay(), monitor) { + public void run() { + Object[] elements = monitor.getElements(); + Set<IRunControl.RunControlContext> set = new HashSet<IRunControl.RunControlContext>(); + for (int i = 0; i < elements.length; i++) { + TCFNode node = null; + if (elements[i] instanceof TCFNode) node = (TCFNode)elements[i]; + else node = model.getRootNode(); + while (node != null && !node.isDisposed()) { + if (!node.validateNode(this)) return; + IRunControl.RunControlContext ctx = node.getRunContext(); + if (ctx != null && ctx.canTerminate()) { + set.add(ctx); + node = null; + } + else { + node = node.getParent(); + if (node == null) set.add(null); + } + } + } + final Set<IToken> cmds = new HashSet<IToken>(); + for (Iterator<IRunControl.RunControlContext> i = set.iterator(); i.hasNext();) { + IRunControl.RunControlContext ctx = i.next(); + if (ctx == null) { + cmds.add(null); + model.getLaunch().terminate(new Runnable() { + public void run() { + assert cmds.contains(null); + cmds.remove(null); + if (cmds.isEmpty()) done(); + } + }); + } + else { + cmds.add(ctx.terminate(new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + assert cmds.contains(token); + cmds.remove(token); + if (error != null) { + monitor.setStatus(new Status(IStatus.ERROR, + TCFUI.PLUGIN_ID, IStatus.OK, "Cannot resume", error)); + } + if (cmds.isEmpty()) done(); + } + })); + } + } + } + }; + return false; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFArgumentsTab.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFArgumentsTab.java new file mode 100644 index 000000000..082d741bc --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFArgumentsTab.java @@ -0,0 +1,197 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.launch; + +import java.net.URL; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.debug.ui.StringVariableSelectionDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Text; + +import com.windriver.debug.tcf.core.launch.TCFLaunchDelegate; +import com.windriver.debug.tcf.ui.TCFUI; + +public class TCFArgumentsTab extends AbstractLaunchConfigurationTab { + + private Text text_arguments; + private Button button_variables; + private Text text_working_dir; + private Button button_default_dir; + private Image image; + + public void createControl(Composite parent) { + Font font = parent.getFont(); + Composite comp = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(1, true); + comp.setLayout(layout); + comp.setFont(font); + + GridData gd = new GridData(GridData.FILL_BOTH); + comp.setLayoutData(gd); + setControl(comp); + + createArgumentsGroup(comp); + createWorkingDirGroup(comp); + + URL url = FileLocator.find(TCFUI.getDefault().getBundle(), + new Path("icons/arguments_tab.gif"), null); + ImageDescriptor descriptor = null; + if (url == null) { + descriptor = ImageDescriptor.getMissingImageDescriptor(); + } + else { + descriptor = ImageDescriptor.createFromURL(url); + } + image = descriptor.createImage(parent.getDisplay()); + } + + private void createArgumentsGroup(Composite comp) { + Font font = comp.getFont(); + + Group group = new Group(comp, SWT.NONE); + group.setFont(font); + group.setLayout(new GridLayout()); + group.setLayoutData(new GridData(GridData.FILL_BOTH)); + group.setText("Program Arguments"); + + text_arguments = new Text(group, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL); + GridData gd = new GridData(GridData.FILL_BOTH); + gd.heightHint = 40; + gd.widthHint = 100; + text_arguments.setLayoutData(gd); + text_arguments.setFont(font); + text_arguments.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent evt) { + updateLaunchConfigurationDialog(); + } + }); + button_variables= createPushButton(group, "Variables", null); + gd = new GridData(GridData.HORIZONTAL_ALIGN_END); + button_variables.setLayoutData(gd); + button_variables.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent arg0) { + handleVariablesButtonSelected(text_arguments); + } + }); + } + + private void createWorkingDirGroup(Composite comp) { + Font font = comp.getFont(); + + Group group = new Group(comp, SWT.NONE); + GridLayout workingDirLayout = new GridLayout(); + workingDirLayout.makeColumnsEqualWidth = false; + group.setLayout(workingDirLayout); + group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + group.setFont(font); + group.setText("Working directory"); + + text_working_dir = new Text(group, SWT.SINGLE | SWT.BORDER); + text_working_dir.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + text_working_dir.setFont(font); + + button_default_dir = new Button(group, SWT.CHECK); + button_default_dir.setText("Use default"); + button_default_dir.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false)); + button_default_dir.setFont(font); + } + + @Override + public void dispose() { + if (image != null) { + image.dispose(); + image = null; + } + super.dispose(); + } + + /** + * A variable entry button has been pressed for the given text + * field. Prompt the user for a variable and enter the result + * in the given field. + */ + private void handleVariablesButtonSelected(Text textField) { + String variable = getVariable(); + if (variable != null) textField.append(variable); + } + + /** + * Prompts the user to choose and configure a variable and returns + * the resulting string, suitable to be used as an attribute. + */ + private String getVariable() { + StringVariableSelectionDialog dialog = new StringVariableSelectionDialog(getShell()); + dialog.open(); + return dialog.getVariableExpression(); + } + + public boolean isValid(ILaunchConfiguration config) { + return true; + } + + public void setDefaults(ILaunchConfigurationWorkingCopy config) { + config.setAttribute(TCFLaunchDelegate.ATTR_PROGRAM_ARGUMENTS, (String)null); + config.setAttribute(TCFLaunchDelegate.ATTR_WORKING_DIRECTORY, (String)null); + } + + public void initializeFrom(ILaunchConfiguration configuration) { + try { + text_arguments.setText(configuration.getAttribute(TCFLaunchDelegate.ATTR_PROGRAM_ARGUMENTS, "")); //$NON-NLS-1$ + text_working_dir.setText(configuration.getAttribute(TCFLaunchDelegate.ATTR_WORKING_DIRECTORY, "")); //$NON-NLS-1$ + } + catch (CoreException e) { + setErrorMessage("Cannot read launch configuration: " + e); + } + } + + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + configuration.setAttribute( + TCFLaunchDelegate.ATTR_PROGRAM_ARGUMENTS, + getAttributeValueFrom(text_arguments)); + configuration.setAttribute( + TCFLaunchDelegate.ATTR_WORKING_DIRECTORY, + getAttributeValueFrom(text_working_dir)); + } + + protected String getAttributeValueFrom(Text text) { + String content = text.getText().trim(); + content = content.replaceAll("\r\n", "\n"); // eliminate Windows \r line delimiter + if (content.length() > 0) return content; + return null; + } + + public String getName() { + return "Arguments"; + } + + @Override + public Image getImage() { + return image; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFMainTab.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFMainTab.java new file mode 100644 index 000000000..3e9168d3c --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFMainTab.java @@ -0,0 +1,718 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.launch; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.ProgressBar; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.swt.widgets.TreeItem; + +import com.windriver.debug.tcf.core.launch.TCFLaunchDelegate; +import com.windriver.debug.tcf.ui.TCFUI; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ILocator; + +/** + * Launch configuration dialog tab to specify the Target Communication Framework + * configuration. + */ +public class TCFMainTab extends AbstractLaunchConfigurationTab { + + private Text peer_id_text; + private Text program_text; + private Tree peer_tree; + private PeerInfo[] peer_info; + private Display display; + + private final Map<LocatorListener,ILocator> listeners = new HashMap<LocatorListener,ILocator>(); + private final Map<String,Image> image_cache = new HashMap<String,Image>(); + + private static class PeerInfo { + PeerInfo parent; + int index; + PeerInfo[] children; + Map<String,String> attrs; + } + + private class LocatorListener implements ILocator.LocatorListener { + + private final PeerInfo parent; + + LocatorListener(PeerInfo parent) { + this.parent = parent; + } + + public void peerAdded(IPeer peer) { + if (display == null) return; + final Map<String,String> attrs = new HashMap<String,String>(peer.getAttributes()); + display.asyncExec(new Runnable() { + public void run() { + PeerInfo[] arr = parent == null ? peer_info : parent.children; + PeerInfo[] buf = new PeerInfo[arr.length + 1]; + System.arraycopy(arr, 0, buf, 0, arr.length); + PeerInfo info = new PeerInfo(); + info.parent = parent; + info.index = arr.length; + info.attrs = attrs; + buf[arr.length] = info; + if (parent == null) { + peer_info = buf; + } + else { + parent.children = buf; + } + updateItems(); + } + }); + } + + public void peerChanged(IPeer peer) { + if (display == null) return; + final Map<String,String> attrs = new HashMap<String,String>(peer.getAttributes()); + display.asyncExec(new Runnable() { + public void run() { + String id = attrs.get(IPeer.ATTR_ID); + PeerInfo[] arr = parent == null ? peer_info : parent.children; + for (int i = 0; i < arr.length; i++) { + if (arr[i].attrs.get(IPeer.ATTR_ID).equals(id)) { + arr[i].attrs = attrs; + updateItems(); + } + } + assert false; + } + }); + } + + public void peerRemoved(final String id) { + if (display == null) return; + display.asyncExec(new Runnable() { + public void run() { + PeerInfo[] arr = parent == null ? peer_info : parent.children; + PeerInfo[] buf = new PeerInfo[arr.length - 1]; + int j = 0; + for (int i = 0; i < arr.length; i++) { + if (!arr[i].attrs.get(IPeer.ATTR_ID).equals(id)) { + buf[j++] = arr[i]; + } + } + if (parent == null) { + peer_info = buf; + } + else { + parent.children = buf; + } + updateItems(); + } + }); + } + + private void updateItems() { + PeerInfo[] arr = null; + TreeItem[] items = null; + if (parent == null) { + arr = peer_info; + peer_tree.setItemCount(arr.length); + items = peer_tree.getItems(); + } + else { + TreeItem item = findItem(parent); + if (item == null) return; + arr = parent.children; + item.setItemCount(arr.length); + items = item.getItems(); + } + assert items.length == arr.length; + for (int i = 0; i < items.length; i++) { + fillItem(items[i], arr[i]); + } + String id = peer_id_text.getText(); + TreeItem item = findItem(findPeerInfo(id)); + if (item != null) peer_tree.setSelection(item); + } + } + + public void createControl(Composite parent) { + display = parent.getDisplay(); + + Font font = parent.getFont(); + Composite comp = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(1, true); + comp.setLayout(layout); + comp.setFont(font); + + GridData gd = new GridData(GridData.FILL_BOTH); + comp.setLayoutData(gd); + setControl(comp); + + createTargetGroup(comp); + createProgramGroup(comp); + } + + private void createTargetGroup(Composite parent) { + Font font = parent.getFont(); + + Group group = new Group(parent, SWT.NONE); + GridLayout top_layout = new GridLayout(); + top_layout.verticalSpacing = 0; + top_layout.numColumns = 2; + group.setLayout(top_layout); + group.setLayoutData(new GridData(GridData.FILL_BOTH)); + group.setFont(font); + group.setText("Target"); + + createVerticalSpacer(group, top_layout.numColumns); + + Label host_label = new Label(group, SWT.NONE); + host_label.setText("Target ID:"); + host_label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); + host_label.setFont(font); + + peer_id_text = new Text(group, SWT.SINGLE | SWT.BORDER); + peer_id_text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + peer_id_text.setFont(font); + peer_id_text.setEditable(false); + peer_id_text.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + updateLaunchConfigurationDialog(); + } + }); + + createVerticalSpacer(group, top_layout.numColumns); + + Label peer_label = new Label(group, SWT.NONE); + peer_label.setText("Available targets:"); + peer_label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); + peer_label.setFont(font); + + if (peer_info == null) loadPeerInfo(null); + + peer_tree = new Tree(group, SWT.VIRTUAL | SWT.BORDER | SWT.SINGLE); + GridData gd = new GridData(GridData.FILL, GridData.FILL, true, true, 2, 1); + gd.minimumHeight = 150; + peer_tree.setLayoutData(gd); + + for (int i = 0; i < 5; i++) { + TreeColumn column = new TreeColumn(peer_tree, SWT.LEAD, i); + column.setMoveable(true); + switch (i) { + case 0: + column.setText("Name"); + column.setWidth(120); + break; + case 1: + column.setText("OS"); + column.setWidth(100); + break; + case 2: + column.setText("Transport"); + column.setWidth(60); + break; + case 3: + column.setText("Host"); + column.setWidth(100); + break; + case 4: + column.setText("Port"); + column.setWidth(40); + break; + } + } + + peer_tree.setHeaderVisible(true); + peer_tree.setFont(font); + peer_tree.setItemCount(peer_info.length); + peer_tree.addListener(SWT.SetData, new Listener() { + public void handleEvent(Event event) { + TreeItem item = (TreeItem)event.item; + PeerInfo info = findPeerInfo(item); + fillItem(item, info); + } + }); + peer_tree.addSelectionListener(new SelectionListener() { + public void widgetDefaultSelected(SelectionEvent e) { + } + public void widgetSelected(SelectionEvent e) { + TreeItem[] selections = peer_tree.getSelection(); + if (selections.length > 0) { + assert selections.length == 1; + PeerInfo info = findPeerInfo(selections[0]); + peer_id_text.setText(getPath(info)); + } + } + }); + + createVerticalSpacer(group, top_layout.numColumns); + + Button button_test = new Button(group, SWT.PUSH); + button_test.setText("Run &Diagnostics"); + button_test.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); + button_test.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + TreeItem[] selection = peer_tree.getSelection(); + if (selection.length > 0) { + assert selection.length == 1; + runDiagnostics(selection[0], false); + } + } + }); + + Button button_loop = new Button(group, SWT.PUSH); + button_loop.setText("Diagnostics &Loop"); + button_loop.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); + button_loop.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + TreeItem[] selection = peer_tree.getSelection(); + if (selection.length > 0) { + assert selection.length == 1; + runDiagnostics(selection[0], true); + } + } + }); + } + + private void createProgramGroup(Composite parent) { + display = parent.getDisplay(); + + Font font = parent.getFont(); + + Group group = new Group(parent, SWT.NONE); + GridLayout top_layout = new GridLayout(); + group.setLayout(top_layout); + group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + group.setFont(font); + group.setText("Program"); + + program_text = new Text(group, SWT.SINGLE | SWT.BORDER); + program_text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + program_text.setFont(font); + } + + @Override + public void dispose() { + Protocol.invokeAndWait(new Runnable() { + public void run() { + for (Iterator<LocatorListener> i = listeners.keySet().iterator(); i.hasNext();) { + LocatorListener listener = i.next(); + listeners.get(listener).removeListener(listener); + } + listeners.clear(); + peer_info = null; + display = null; + } + }); + for (Image i : image_cache.values()) i.dispose(); + image_cache.clear(); + super.dispose(); + } + + public String getName() { + return "Main"; + } + + @Override + public Image getImage() { + return getImage("icons/tcf.gif"); + } + + public void initializeFrom(ILaunchConfiguration configuration) { + try { + String id = configuration.getAttribute( + TCFLaunchDelegate.ATTR_PEER_ID, (String)null); + if (id != null) { + peer_id_text.setText(id); + TreeItem item = findItem(findPeerInfo(id)); + if (item != null) peer_tree.setSelection(item); + } + program_text.setText(configuration.getAttribute( + TCFLaunchDelegate.ATTR_PROGRAM_FILE, "")); //$NON-NLS-1$ + } + catch (CoreException e) { + setErrorMessage(e.getMessage()); + } + } + + public boolean isValid(ILaunchConfiguration launchConfig) { + String id = peer_id_text.getText().trim(); + if (id.length() == 0) { + setErrorMessage("Specify a target ID"); + return false; + } + setErrorMessage(null); + return super.isValid(launchConfig); + } + + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + String id = peer_id_text.getText().trim(); + if (id.length() == 0) id = null; + configuration.setAttribute(TCFLaunchDelegate.ATTR_PEER_ID, id); + String nm = program_text.getText().trim(); + if (nm.length() == 0) nm = null; + configuration.setAttribute(TCFLaunchDelegate.ATTR_PROGRAM_FILE, nm); + } + + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + configuration.setAttribute(TCFLaunchDelegate.ATTR_PEER_ID, "TCFLocal"); + configuration.setAttribute(TCFLaunchDelegate.ATTR_PROGRAM_FILE, (String)null); + } + + private void loadPeerInfo(final PeerInfo parent) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + if (parent == null) { + ILocator locator = Protocol.getLocator(); + Map<String,IPeer> map = locator.getPeers(); + PeerInfo[] buf = new PeerInfo[map.size()]; + int n = 0; + for (Iterator<IPeer> i = map.values().iterator(); i.hasNext();) { + IPeer p = i.next(); + PeerInfo info = new PeerInfo(); + info.parent = parent; + info.index = n; + info.attrs = new HashMap<String,String>(p.getAttributes()); + buf[n++] = info; + } + LocatorListener listener = new LocatorListener(parent); + listeners.put(listener, locator); + locator.addListener(listener); + assert peer_info == null; + peer_info = buf; + } + else { + PeerInfo[] buf = new PeerInfo[0]; + assert parent.children == null; + parent.children = buf; + } + } + }); + } + + private PeerInfo findPeerInfo(TreeItem item) { + TreeItem parent = item.getParentItem(); + if (parent == null) return peer_info[peer_tree.indexOf(item)]; + PeerInfo info = findPeerInfo(parent); + if (info.children == null) loadPeerInfo(info); + return info.children[parent.indexOf(item)]; + } + + private PeerInfo findPeerInfo(String path) { + int i = path.lastIndexOf('/'); + String id = null; + PeerInfo[] arr = null; + if (i < 0) { + if (peer_info == null) loadPeerInfo(null); + arr = peer_info; + id = path; + } + else { + PeerInfo p = findPeerInfo(path.substring(0, i)); + if (p == null) return null; + if (p.children == null) loadPeerInfo(p); + arr = p.children; + id = path.substring(i + 1); + } + for (int n = 0; n < arr.length; n++) { + if (arr[n].attrs.get(IPeer.ATTR_ID).equals(id)) return arr[n]; + } + return null; + } + + private TreeItem findItem(PeerInfo info) { + if (info == null) return null; + if (info.parent == null) { + return peer_tree.getItem(info.index); + } + TreeItem i = findItem(info.parent); + if (i == null) return null; + peer_tree.showItem(i); + return i.getItem(info.index); + } + + private interface DoneFindPeer { + void doneFindPeer(Collection<Throwable> errors, IPeer peer); + } + + private void findPeer(TreeItem item, final DoneFindPeer done) { + assert display != null; + assert Thread.currentThread() == display.getThread(); + final String path = getPath(findPeerInfo(item)); + Protocol.invokeLater(new Runnable() { + public void run() { + final Collection<Throwable> errors = new ArrayList<Throwable>(); + try { + final int i = path.lastIndexOf('/'); + if (i < 0) { + done.doneFindPeer(errors, Protocol.getLocator().getPeers().get(path)); + } + else { + openChannel(path.substring(0, i), errors, new DoneOpenChannel() { + public void doneOpenChannel(IChannel channel) { + IPeer peer = null; + if (channel != null) { + ILocator locator = channel.getRemoteService(ILocator.class); + peer = locator.getPeers().get(path.substring(i + 1)); + channel.close(); + } + done.doneFindPeer(errors, peer); + } + }); + } + } + catch (Throwable x) { + errors.add(x); + done.doneFindPeer(errors, null); + } + } + }); + } + + private interface DoneOpenChannel { + void doneOpenChannel(IChannel channel); + } + + private static class OpenChannelListener implements IChannel.IChannelListener { + + private final Collection<Throwable> errors; + private final IChannel channel; + private final DoneOpenChannel done; + + OpenChannelListener(Collection<Throwable> errors, IChannel channel, DoneOpenChannel done) { + this.errors = errors; + this.channel = channel; + this.done = done; + channel.addChannelListener(this); + } + + public void onChannelOpened() { + channel.removeChannelListener(this); + done.doneOpenChannel(channel); + } + + public void congestionLevel(int level) { + } + + public void onChannelClosed(Throwable e) { + errors.add(e); + channel.removeChannelListener(this); + done.doneOpenChannel(null); + } + } + + private void openChannel(String path, final Collection<Throwable> errors, final DoneOpenChannel done) { + assert Protocol.isDispatchThread(); + try { + int i = path.lastIndexOf('/'); + if (i < 0) { + IPeer peer = Protocol.getLocator().getPeers().get(path); + if (peer == null) { + errors.add(new Exception("Peer not found: " + path)); + done.doneOpenChannel(null); + return; + } + new OpenChannelListener(errors, peer.openChannel(), done); + } + else { + final String id = path.substring(i + 1); + openChannel(path.substring(0, i), errors, new DoneOpenChannel() { + public void doneOpenChannel(IChannel channel) { + if (errors.size() > 0) { + if (channel != null) channel.close(); + done.doneOpenChannel(null); + } + else { + channel.redirect(id); + new OpenChannelListener(errors, channel, done); + } + } + }); + } + } + catch (Throwable x) { + errors.add(x); + done.doneOpenChannel(null); + } + } + + private void runDiagnostics(TreeItem item, boolean loop) { + final Shell shell = new Shell(getShell(), SWT.TITLE | SWT.PRIMARY_MODAL); + GridLayout layout = new GridLayout(); + layout.verticalSpacing = 0; + layout.numColumns = 2; + shell.setLayout(layout); + shell.setText("Running Diagnostics..."); + CLabel label = new CLabel(shell, SWT.NONE); + label.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false)); + label.setText("Running Diagnostics..."); + final TCFSelfTest[] test = new TCFSelfTest[1]; + Button button_cancel = new Button(shell, SWT.PUSH); + button_cancel.setText("&Cancel"); + button_cancel.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); + button_cancel.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + Protocol.invokeLater(new Runnable() { + public void run() { + if (test[0] != null) test[0].cancel(); + } + }); + } + }); + createVerticalSpacer(shell, 0); + ProgressBar bar = new ProgressBar(shell, SWT.HORIZONTAL); + bar.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 2, 1)); + shell.setDefaultButton(button_cancel); + shell.pack(); + shell.setSize(483, shell.getSize().y); + Rectangle rc0 = getShell().getBounds(); + Rectangle rc1 = shell.getBounds(); + shell.setLocation(rc0.x + (rc0.width - rc1.width) / 2, rc0.y + (rc0.height - rc1.height) / 2); + shell.setVisible(true); + runDiagnostics(item, loop, test, shell, label, bar); + } + + private void runDiagnostics(final TreeItem item, final boolean loop, final TCFSelfTest[] test, + final Shell shell, final CLabel label, final ProgressBar bar) { + final TCFSelfTest.TestListener done = new TCFSelfTest.TestListener() { + private String last_text = ""; + private int last_count = 0; + private int last_total = 0; + public void progress(final String label_text, final int count_done, final int count_total) { + if ((label_text == null || last_text.equals(label_text)) && + last_total == count_total && + (count_done - last_count) / (float)count_total < 0.02f) return; + if (label_text != null) last_text = label_text; + last_total = count_total; + last_count = count_done; + display.asyncExec(new Runnable() { + public void run() { + label.setText(last_text); + bar.setMinimum(0); + bar.setMaximum(last_total); + bar.setSelection(last_count); + } + }); + } + public void done(final Collection<Throwable> errors) { + final boolean b = test[0] == null ? false : test[0].isCanceled(); + test[0] = null; + display.asyncExec(new Runnable() { + public void run() { + if (errors.size() > 0) { + shell.dispose(); + new TestErrorsDialog(getControl().getShell(), + getImage("icons/tcf.gif"), errors).open(); + } + else if (loop && !b) { + runDiagnostics(item, true, test, shell, label, bar); + } + else { + shell.dispose(); + } + } + }); + } + }; + findPeer(item, new DoneFindPeer() { + public void doneFindPeer(Collection<Throwable> errors, IPeer peer) { + if (errors.size() > 0) { + done.done(errors); + } + else { + try { + test[0] = new TCFSelfTest(peer, done); + } + catch (Throwable x) { + errors.add(x); + done.done(errors); + } + } + } + }); + } + + private void fillItem(TreeItem item, PeerInfo info) { + String text[] = new String[5]; + text[0] = info.attrs.get(IPeer.ATTR_NAME); + text[1] = info.attrs.get(IPeer.ATTR_OS_NAME); + text[2] = info.attrs.get(IPeer.ATTR_TRANSPORT_NAME); + text[3] = info.attrs.get(IPeer.ATTR_IP_HOST); + text[4] = info.attrs.get(IPeer.ATTR_IP_PORT); + item.setText(text); + item.setImage(getImage(getImageName(info))); + if (info.children == null) loadPeerInfo(info); + item.setItemCount(info.children.length); + } + + private String getPath(PeerInfo info) { + String id = info.attrs.get(IPeer.ATTR_ID); + if (info.parent == null) return id; + return getPath(info.parent) + "/" + id; + } + + private Image getImage(String name) { + if (name == null) return null; + if (display == null) return null; + Image image = image_cache.get(name); + if (image == null) { + URL url = FileLocator.find(TCFUI.getDefault().getBundle(), new Path(name), null); + ImageDescriptor descriptor = null; + if (url == null) { + descriptor = ImageDescriptor.getMissingImageDescriptor(); + } + else { + descriptor = ImageDescriptor.createFromURL(url); + } + image = descriptor.createImage(display); + image_cache.put(name, image); + } + return image; + } + + private String getImageName(PeerInfo info) { + return "icons/tcf.gif"; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFSelfTest.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFSelfTest.java new file mode 100644 index 000000000..84208836d --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFSelfTest.java @@ -0,0 +1,1207 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.launch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IBreakpoints; +import com.windriver.tcf.api.services.IDiagnostics; +import com.windriver.tcf.api.services.IFileSystem; +import com.windriver.tcf.api.services.ILineNumbers; +import com.windriver.tcf.api.services.IMemory; +import com.windriver.tcf.api.services.IRegisters; +import com.windriver.tcf.api.services.IRunControl; +import com.windriver.tcf.api.services.IDiagnostics.ISymbol; +import com.windriver.tcf.api.services.IFileSystem.DirEntry; +import com.windriver.tcf.api.services.IFileSystem.FileAttrs; +import com.windriver.tcf.api.services.IFileSystem.FileSystemException; +import com.windriver.tcf.api.services.IFileSystem.IFileHandle; +import com.windriver.tcf.api.services.ILineNumbers.CodeArea; +import com.windriver.tcf.api.services.IMemory.MemoryContext; +import com.windriver.tcf.api.services.IMemory.MemoryError; +import com.windriver.tcf.api.services.IRegisters.RegistersContext; +import com.windriver.tcf.api.services.IRunControl.RunControlContext; +import com.windriver.tcf.api.util.TCFFileInputStream; +import com.windriver.tcf.api.util.TCFFileOutputStream; + +class TCFSelfTest { + + private final static int NUM_CHANNELS = 4; + + private final TestListener listener; + private final IChannel[] channels; + private final LinkedList<Runnable> pending_tests = new LinkedList<Runnable>(); + private final Map<Test,IChannel> active_tests = new HashMap<Test,IChannel>(); + private final Collection<Throwable> errors = new ArrayList<Throwable>(); + + private int count_total; + private int count_done; + private boolean canceled; + private boolean memory_lock; + + public interface TestListener { + public void progress(String label, int done, int total); + public void done(Collection<Throwable> errors); + } + + @SuppressWarnings("serial") + private static class CancelException extends Exception { + CancelException() { + super("Canceled"); + } + } + + private interface Test { + } + + TCFSelfTest(IPeer peer, TestListener listener) throws IOException { + this.listener = listener; + pending_tests.add(new Runnable() { + public void run() { + for (IChannel channel : channels) new TestEcho(channel); + } + }); + pending_tests.add(new Runnable() { + public void run() { + int i = 0; + for (IChannel channel : channels) new TestRCBP1(channel, i++); + } + }); + pending_tests.add(new Runnable() { + public void run() { + for (IChannel channel : channels) new TestFileSystem(channel); + } + }); + pending_tests.add(new Runnable() { + public void run() { + for (int i = 0; i < channels.length; i++) { + switch (i % 3) { + case 0: new TestEcho(channels[i]); break; + case 1: new TestRCBP1(channels[i], i); break; + case 2: new TestFileSystem(channels[i]); break; + } + } + } + }); + count_total = NUM_CHANNELS * pending_tests.size() * 2; + channels = new IChannel[NUM_CHANNELS]; + listener.progress("Openning communication channels...", count_done, count_total); + for (int i = 0; i < channels.length; i++) { + final IChannel channel = channels[i] = peer.openChannel(); + channel.addChannelListener(new IChannel.IChannelListener() { + + public void onChannelOpened() { + for (int i = 0; i < channels.length; i++) { + if (channels[i] == null) return; + if (channels[i].getState() != IChannel.STATE_OPEN) return; + } + runNextTest(); + } + + public void congestionLevel(int level) { + } + + public void onChannelClosed(Throwable error) { + channel.removeChannelListener(this); + if (error == null && (!active_tests.isEmpty() || !pending_tests.isEmpty())) { + error = new IOException("Remote peer closed connection before all tests finished"); + } + int cnt = 0; + for (int i = 0; i < channels.length; i++) { + if (channels[i] == channel) { + channels[i] = null; + if (error != null && !(error instanceof CancelException)) errors.add(error); + for (Iterator<Test> n = active_tests.keySet().iterator(); n.hasNext();) { + if (active_tests.get(n.next()) == channel) n.remove(); + } + } + else if (channels[i] != null) { + cnt++; + } + } + if (cnt == 0) { + TCFSelfTest.this.listener.done(errors); + } + else if (active_tests.isEmpty()) { + for (int i = 0; i < channels.length; i++) { + if (channels[i] != null && channels[i].getState() != IChannel.STATE_CLOSED) { + if (errors.isEmpty()) channels[i].close(); + else channels[i].terminate(new CancelException()); + } + } + } + } + }); + } + } + + void cancel() { + if (canceled) return; + for (final Test t : active_tests.keySet()) { + if (t instanceof TestRCBP1) { + ((TestRCBP1)t).cancel(new Runnable() { + public void run() { + assert active_tests.get(t) == null; + cancel(); + } + }); + return; + } + } + canceled = true; + for (IChannel c : channels) { + if (c != null && c.getState() != IChannel.STATE_CLOSED) { + c.terminate(new CancelException()); + } + } + } + + boolean isCanceled() { + return canceled; + } + + private class TestEcho implements Test, IDiagnostics.DoneEcho { + + private final IDiagnostics diag; + private final LinkedList<String> msgs = new LinkedList<String>(); + private int count = 0; + + TestEcho(IChannel channel) { + diag = channel.getRemoteService(IDiagnostics.class); + listener.progress("Running Echo Test...", ++count_done, count_total); + active_tests.put(this, channel); + if (diag == null) { + done(this); + } + else { + for (int i = 0; i < 32; i++) sendMessage(); + } + } + + private void sendMessage() { + StringBuffer buf = new StringBuffer(); + buf.append(Integer.toHexString(count)); + for (int i = 0; i < 64; i++) { + buf.append('-'); + buf.append((char)(0x400 * i + count)); + } + String s = buf.toString(); + msgs.add(s); + diag.echo(s, this); + count++; + } + + public void doneEcho(IToken token, Throwable error, String b) { + String s = msgs.removeFirst(); + if (active_tests.get(this) == null) return; + if (error != null) { + errors.add(error); + done(this); + } + else if (!s.equals(b)) { + errors.add(new Exception("Echo test failed: " + s + " != " + b)); + done(this); + } + else if (count < 0x400) { + sendMessage(); + } + else if (msgs.isEmpty()){ + done(this); + } + } + } + + private class TestRCBP1 implements Test, + IDiagnostics.DoneGetTestList, IDiagnostics.DoneRunTest, + IRunControl.DoneGetContext, IRunControl.DoneGetChildren, + IRunControl.DoneGetState, IRunControl.RunControlListener, + IDiagnostics.DoneGetSymbol { + + private final int channel_id; + private final IDiagnostics diag; + private final IMemory mm; + private final IRunControl rc; + private final IRegisters rg; + private final IBreakpoints bp; + private final ILineNumbers ln; + private final Map<String,IRunControl.RunControlContext> threads = new HashMap<String,IRunControl.RunControlContext>(); + private final Map<String,SuspendedContext> suspended = new HashMap<String,SuspendedContext>(); + private final Map<String,SuspendedContext> suspended_prev = new HashMap<String,SuspendedContext>(); + private final Set<String> running = new HashSet<String>(); + private final Map<IToken,String> get_state_cmds = new HashMap<IToken,String>(); + private final Map<String,Map<String,IRegisters.RegistersContext>> regs = + new HashMap<String,Map<String,IRegisters.RegistersContext>>(); + + private String context_id; // Test process context ID + private IRunControl.RunControlContext context; + private String main_thread_id; + private Runnable pending_cancel; + private ISymbol func0; + private ISymbol func1; + private ISymbol func2; + private ISymbol array; + private int bp_cnt = 0; + + private class SuspendedContext { + final String id; + final String pc; + final String reason; + final Map<String,Object> params; + boolean resumed; + + SuspendedContext(String id, String pc, String reason, Map<String,Object> params) { + this.id = id; + this.pc = pc; + this.reason = reason; + this.params = params; + } + } + + TestRCBP1(IChannel channel, int channel_id) { + this.channel_id = channel_id; + diag = channel.getRemoteService(IDiagnostics.class); + mm = channel.getRemoteService(IMemory.class); + rc = channel.getRemoteService(IRunControl.class); + rg = channel.getRemoteService(IRegisters.class); + bp = channel.getRemoteService(IBreakpoints.class); + ln = channel.getRemoteService(ILineNumbers.class); + active_tests.put(this, channel); + listener.progress("Running Run Control Test...", ++count_done, count_total); + if (diag == null || rc == null) { + done(this); + } + else if (bp == null) { + exit(new Exception("Remote Breakpoints service not found")); + } + else { + diag.getTestList(this); + } + } + + public void doneGetTestList(IToken token, Throwable error, String[] list) { + assert active_tests.get(this) != null; + if (error != null) { + exit(error); + } + else { + for (int i = 0; i < list.length; i++) { + if (list[i].equals("RCBP1")) { + diag.runTest("RCBP1", this); + return; + } + } + } + exit(null); + } + + public void doneRunTest(IToken token, Throwable error, String context_id) { + assert active_tests.get(this) != null; + assert this.context_id == null; + if (error != null) { + exit(error); + } + else { + this.context_id = context_id; + if (pending_cancel != null) { + Protocol.invokeLater(pending_cancel); + pending_cancel = null; + } + else { + diag.getSymbol(context_id, "tcf_test_func0", this); + diag.getSymbol(context_id, "tcf_test_func1", this); + diag.getSymbol(context_id, "tcf_test_func2", this); + diag.getSymbol(context_id, "tcf_test_array", this); + } + } + } + + @SuppressWarnings("unchecked") + public void doneGetSymbol(IToken token, Throwable error, ISymbol symbol) { + assert active_tests.get(this) != null; + assert this.context_id != null; + if (error != null) { + exit(error); + } + else if (!symbol.isGlobal()) { + exit(new Exception("Symbols 'tcf_test_*' must be global")); + } + else if (!symbol.isAbs()) { + exit(new Exception("Symbols 'tcf_test_*' must be absolute")); + } + else if (func0 == null) { + func0 = symbol; + } + else if (func1 == null) { + func1 = symbol; + } + else if (func2 == null) { + func2 = symbol; + } + else { + array = symbol; + Map<String,Object> m[] = new Map[4]; + for (int i = 0; i < m.length; i++) { + m[i] = new HashMap(); + m[i].put(IBreakpoints.PROP_ID, "TcfTestBP" + i); + m[i].put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); + switch (i) { + case 0: + m[i].put(IBreakpoints.PROP_ADDRESS, func0.getValue().toString()); + m[i].put(IBreakpoints.PROP_CONDITION, "$thread!=\"\""); + break; + case 1: + m[i].put(IBreakpoints.PROP_ADDRESS, "(31+1)/16+tcf_test_func1-2"); + m[i].put(IBreakpoints.PROP_CONDITION, "tcf_test_func0!=tcf_test_func1"); + break; + case 2: + m[i].put(IBreakpoints.PROP_ADDRESS, "tcf_test_func2"); + m[i].put(IBreakpoints.PROP_ENABLED, Boolean.FALSE); + break; + case 3: + m[i].put(IBreakpoints.PROP_ID, "TcfTestBP3" + channel_id); + m[i].put(IBreakpoints.PROP_ENABLED, Boolean.FALSE); + m[i].put(IBreakpoints.PROP_ADDRESS, "tcf_test_func2"); + break; + } + } + bp.set(m, new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) { + exit(error); + } + else { + rc.getContext(context_id, TestRCBP1.this); + } + } + }); + } + } + + public void doneGetContext(IToken token, Exception error, RunControlContext context) { + if (canceled) return; + if (error != null) { + exit(error); + } + else { + if (this.context == null) { + this.context = context; + assert context_id.equals(context.getID()); + assert threads.isEmpty(); + assert running.isEmpty(); + assert suspended.isEmpty(); + rc.addListener(this); + } + rc.getChildren(context.getID(), this); + if (context.hasState()) { + threads.put(context.getID(), context); + get_state_cmds.put(context.getState(this), context.getID()); + } + } + } + + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + if (canceled) return; + if (error != null) { + exit(error); + } + else { + for (String id : contexts) rc.getContext(id, this); + } + } + + public void doneGetState(IToken token, Exception error, + boolean suspended, String pc, String reason, + Map<String, Object> params) { + final String id = get_state_cmds.remove(token); + if (canceled) return; + if (id == null) { + exit(new Exception("Invalid getState responce")); + } + else if (!suspended) { + if (this.suspended.get(id) != null) { + exit(new Exception("Invalid result of getState command")); + } + else { + running.add(id); + } + } + else { + assert threads.get(id) != null; + if (running.contains(id)) { + exit(new Exception("Invalid result of getState command")); + } + else { + SuspendedContext sc = this.suspended.get(id); + if (sc != null) { + if (!sc.pc.equals(pc) || !sc.reason.equals(reason)) { + exit(new Exception("Invalid result of getState command")); + } + else { + resume(sc); + } + } + else { + if (main_thread_id != null) { + exit(new Exception("Missing contextSuspended event for " + id)); + } + else if ("Breakpoint".equals(reason)) { + exit(new Exception("Invalid suspend reason of main thread after test start: " + reason + " " + pc)); + } + else { + main_thread_id = id; + final SuspendedContext sx = new SuspendedContext(id, pc, reason, params); + this.suspended.put(id, sx); + final String bp_id = "TcfTestBP3" + channel_id; + Map<String,Object> m = new HashMap<String,Object>(); + m.put(IBreakpoints.PROP_ID, bp_id); + m.put(IBreakpoints.PROP_ENABLED, Boolean.FALSE); + m.put(IBreakpoints.PROP_ADDRESS, "tcf_test_func2"); + m.put(IBreakpoints.PROP_CONDITION, "$thread==\"" + id + "\""); + bp.change(m, new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); + } + }); + Protocol.sync(new Runnable() { + public void run() { + bp.enable(new String[]{ bp_id }, new IBreakpoints.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) exit(error); + } + }); + resume(sx); + } + }); + } + } + } + } + } + + public void containerResumed(String[] context_ids) { + for (String id : context_ids) contextResumed(id); + } + + public void containerSuspended(String context, String pc, + String reason, Map<String, Object> params, + String[] suspended_ids) { + for (String id : suspended_ids) { + if (id.equals(context)) continue; + contextSuspended(id, null, null, null); + } + contextSuspended(context, pc, reason, params); + } + + public void contextAdded(RunControlContext[] contexts) { + for (int i = 0; i < contexts.length; i++) { + if (threads.get(contexts[i].getID()) != null) { + exit(new Exception("Invalid contextAdded event")); + return; + } + if (context.getID().equals(contexts[i].getProperties().get("ParentID"))) { + threads.put(contexts[i].getID(), contexts[i]); + running.add(contexts[i].getID()); + } + } + } + + public void contextChanged(RunControlContext[] contexts) { + for (int i = 0; i < contexts.length; i++) { + if (contexts[i].getID().equals(context.getID())) { + context = contexts[i]; + } + if (context.getID().equals(contexts[i].getProperties().get("ProcessID"))) { + threads.put(contexts[i].getID(), contexts[i]); + } + } + } + + public void contextException(String context, String msg) { + if (context.equals(this.context.getID()) || threads.get(context) != null) { + exit(new Exception(msg)); + } + } + + public void contextRemoved(String[] contexts) { + for (String id : contexts) { + if (suspended.get(id) != null) { + exit(new Exception("Invalid contextRemoved event")); + return; + } + threads.remove(id); + running.remove(id); + if (threads.isEmpty()) { + if (bp_cnt != 30) { + exit(new Exception("Test main thread breakpoint count = " + bp_cnt + ", expected 30")); + } + rc.removeListener(this); + // Flush communication channel of pending commands + Protocol.sync(new Runnable() { + public void run() { + exit(null); + } + }); + } + } + } + + public void contextResumed(String id) { + if (threads.get(id) == null) return; + SuspendedContext sc = suspended.remove(id); + if (!isAlienBreakpoint(sc)) suspended_prev.put(id, sc); + running.add(id); + } + + private String toSymName(long addr) { + if (func0.getValue().longValue() == addr) return "tcf_test_func0"; + if (func1.getValue().longValue() == addr) return "tcf_test_func1"; + if (func2.getValue().longValue() == addr) return "tcf_test_func2"; + return "*no name*"; + } + + private void checkSuspendedContext(SuspendedContext sp, ISymbol sym) { + long pc = Long.parseLong(sp.pc); + if (pc != sym.getValue().longValue() || !"Breakpoint".equals(sp.reason)) { + exit(new Exception("Invalid contextSuspended event: " + sp.id + " '" + toSymName(pc) + "' " + sp.pc + " " + sp.reason + + ", expected breakpoint at '" + toSymName(sym.getValue().longValue()) + "' " + sym.getValue())); + } + } + + private boolean isAlienBreakpoint(SuspendedContext sc) { + // Check if context suspended by a breakpoint from another debug session + // Test should ignore such breakpoints. + if (!"Breakpoint".equals(sc.reason)) return false; + long pc = Long.parseLong(sc.pc); + if (pc == func0.getValue().longValue()) return false; + if (pc == func1.getValue().longValue()) return false; + if (pc == func2.getValue().longValue()) return false; + return true; + } + + public void contextSuspended(String id, String pc, String reason, Map<String, Object> params) { + if (threads.get(id) == null) return; + assert main_thread_id != null; + running.remove(id); + SuspendedContext sc = suspended.get(id); + if (sc != null) { + if (!sc.pc.equals(pc) || !sc.reason.equals(reason)) { + exit(new Exception("Invalid contextSuspended event")); + } + } + else { + sc = new SuspendedContext(id, pc, reason, params); + suspended.put(id, sc); + if (!isAlienBreakpoint(sc)) { + if ("Breakpoint".equals(reason) && id.equals(main_thread_id)) bp_cnt++; + SuspendedContext sp = suspended_prev.get(id); + if (sp != null) { + if (Long.parseLong(sc.pc) == func2.getValue().longValue()) { + checkSuspendedContext(sp, func1); + } + else if (Long.parseLong(sc.pc) == func1.getValue().longValue()) { + checkSuspendedContext(sp, func0); + } + else if (Long.parseLong(sc.pc) == func0.getValue().longValue()) { + if (id.equals(main_thread_id)) { + if ("Breakpoint".equals(sp.reason)) { + checkSuspendedContext(sp, func2); + } + } + else { + checkSuspendedContext(sp, func1); + } + } + } + } + } + final SuspendedContext sc0 = sc; + ILineNumbers.DoneMapToSource ln_done = new ILineNumbers.DoneMapToSource() { + public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) { + if (error != null) exit(error); + if (mm != null) runMemoryTest(sc0); + else if (rg != null) runRegistersTest(sc0); + else resume(sc0); + } + }; + if (ln != null) { + BigInteger x = new BigInteger(pc); + BigInteger y = x.add(BigInteger.valueOf(1)); + ln.mapToSource(id, x, y, ln_done); + } + else { + ln_done.doneMapToSource(null, null, null); + } + } + + private void resume(final SuspendedContext sc) { + IRunControl.RunControlContext ctx = threads.get(sc.id); + if (ctx != null && !sc.resumed) { + sc.resumed = true; + ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (canceled) return; + if (active_tests.get(this) == null) return; + if (threads.get(sc.id) == null) return; + if (error != null) exit(error); + } + }); + } + } + + private void runMemoryTest(final SuspendedContext sc) { + if (memory_lock) { + resume(sc); + return; + } + memory_lock = true; + mm.getContext(context_id, new IMemory.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, final MemoryContext mem_ctx) { + if (error != null) { + exit(error); + return; + } + if (!context_id.equals(mem_ctx.getID())) { + exit(new Exception("Bad memory context data: invalid ID")); + } + Object pid = context.getProperties().get("ProcessID"); + if (pid != null && !pid.equals(mem_ctx.getProperties().get("ProcessID"))) { + exit(new Exception("Bad memory context data: invalid ProcessID")); + } + final boolean big_endian = mem_ctx.isBigEndian(); + final int addr_size = mem_ctx.getAddressSize(); + final byte[] buf = new byte[0x1000]; + mem_ctx.get(array.getValue(), 1, buf, 0, addr_size, 0, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + byte[] tmp = new byte[addr_size + 1]; + tmp[0] = 0; // Extra byte to avoid sign extension by BigInteger + if (big_endian) { + System.arraycopy(buf, 0, tmp, 1, addr_size); + } + else { + for (int i = 0; i < addr_size; i++) { + tmp[i + 1] = buf[addr_size - i - 1]; + } + } + Number mem_address = new BigInteger(tmp); + testSetMemoryCommand(sc, mem_ctx, mem_address, buf); + } + }); + } + }); + } + + private void testSetMemoryCommand(final SuspendedContext sc, + final IMemory.MemoryContext mem_ctx, + final Number addr, final byte[] buf) { + final byte[] data = new byte[buf.length]; + new Random().nextBytes(data); + mem_ctx.set(addr, 1, data, 0, data.length, 0, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + if (error != null) { + exit(error); + return; + } + mem_ctx.get(addr, 1, buf, 0, buf.length, 0, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + if (error != null) { + exit(error); + return; + } + for (int i = 0; i < data.length; i++) { + if (data[i] != buf[i]) { + exit(new Exception( + "Invalid Memory.get responce: wrong data at offset " + i + + ", expected " + data[i] + ", actual " + buf[i])); + return; + } + } + testFillMemoryCommand(sc, mem_ctx, addr, buf); + } + }); + } + }); + } + + private void testFillMemoryCommand(final SuspendedContext sc, + final IMemory.MemoryContext mem_ctx, + final Number addr, final byte[] buf) { + final byte[] data = new byte[buf.length / 7]; + new Random().nextBytes(data); + mem_ctx.fill(addr, 1, data, buf.length, 0, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + if (error != null) { + exit(error); + return; + } + mem_ctx.get(addr, 1, buf, 0, buf.length, 0, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + if (error != null) { + exit(error); + return; + } + for (int i = 0; i < data.length; i++) { + if (data[i % data.length] != buf[i]) { + exit(new Exception( + "Invalid Memory.get responce: wrong data at offset " + i + + ", expected " + data[i % data.length] + ", actual " + buf[i])); + return; + } + } + memory_lock = false; + if (rg != null) runRegistersTest(sc); + else resume(sc); + } + }); + } + }); + } + + private void runRegistersTest(final SuspendedContext sc) { + if (regs.get(sc.id) == null) { + final Map<String,IRegisters.RegistersContext> reg_map = + new HashMap<String,IRegisters.RegistersContext>(); + final Set<IToken> cmds = new HashSet<IToken>(); + regs.put(sc.id, reg_map); + cmds.add(rg.getChildren(sc.id, new IRegisters.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] context_ids) { + cmds.remove(token); + if (error != null) { + for (IToken t : cmds) t.cancel(); + exit(error); + return; + } + for (final String id : context_ids) { + cmds.add(rg.getChildren(id, this)); + cmds.add(rg.getContext(id, new IRegisters.DoneGetContext() { + public void doneGetContext(IToken token, + Exception error, + RegistersContext context) { + cmds.remove(token); + if (error != null) { + for (IToken t : cmds) t.cancel(); + exit(error); + return; + } + reg_map.put(id, context); + if (cmds.isEmpty()) { + testGetSetRegisterCommands(sc); + } + } + })); + } + } + })); + } + else { + testGetSetRegisterCommands(sc); + } + } + + private void testGetSetRegisterCommands(final SuspendedContext sc) { + final Set<IToken> cmds = new HashSet<IToken>(); + Map<String,IRegisters.RegistersContext> reg_map = regs.get(sc.id); + for (final IRegisters.RegistersContext ctx : reg_map.values()) { + if (!ctx.isReadable()) continue; + if (ctx.isReadOnce()) continue; + String[] fmts = ctx.getAvailableFormats(); + for (final String fmt : fmts) { + cmds.add(ctx.get(fmt, new IRegisters.DoneGet() { + public void doneGet(IToken token, Exception error, String value) { + cmds.remove(token); + if (error != null) { + for (IToken t : cmds) t.cancel(); + exit(error); + return; + } + cmds.add(ctx.set(fmt, value, new IRegisters.DoneSet() { + public void doneSet(IToken token, Exception error) { + cmds.remove(token); + if (error != null) { + for (IToken t : cmds) t.cancel(); + exit(error); + return; + } + if (cmds.isEmpty()) { + resume(sc); + } + } + })); + } + })); + } + } + if (cmds.isEmpty()) { + resume(sc); + } + } + + void cancel(final Runnable done) { + if (rc != null) rc.removeListener(this); + if (context_id == null) { + if (pending_cancel != null) { + exit(null); + } + else { + pending_cancel = done; + } + } + else { + diag.cancelTest(context_id, new IDiagnostics.DoneCancelTest() { + public void doneCancelTest(IToken token, Throwable error) { + exit(error); + done.run(); + } + }); + } + } + + private void exit(Throwable x) { + if (active_tests.get(this) == null) return; + if (pending_cancel != null) { + pending_cancel.run(); + pending_cancel = null; + } + else { + if (x != null) errors.add(x); + if (rc != null) rc.removeListener(this); + } + done(this); + } + } + + private int file_count = 0; + + private class TestFileSystem implements Test, IFileSystem.DoneStat, + IFileSystem.DoneOpen, IFileSystem.DoneClose, + IFileSystem.DoneWrite, IFileSystem.DoneRead, + IFileSystem.DoneRename, IFileSystem.DoneRealPath, + IFileSystem.DoneRemove, IFileSystem.DoneRoots, + IFileSystem.DoneReadDir { + + private static final int + STATE_PRE = 0, + STATE_WRITE = 1, + STATE_READ = 2, + STATE_OUT = 3, + STATE_INP = 4, + STATE_EXIT = 5; + + private final IFileSystem files; + private final byte[] data = new byte[0x1000]; + private String root; + private String tmp_path; + private String file_name; + private IFileHandle handle; + private int state = STATE_PRE; + + TestFileSystem(IChannel channel) { + files = channel.getRemoteService(IFileSystem.class); + active_tests.put(this, channel); + listener.progress("Running File System Test...", ++count_done, count_total); + if (files == null) { + done(this); + } + else { + files.roots(this); + } + } + + public void doneRoots(IToken token, FileSystemException error, DirEntry[] entries) { + assert state == STATE_PRE; + if (error != null) { + error(error); + } + else if (entries == null || entries.length == 0) { + error(new Exception("Invalid FileSysrem.roots responce: empty roots array")); + } + else { + root = entries[0].filename; + files.opendir(root, this); + } + } + + public void doneReadDir(IToken token, FileSystemException error, + DirEntry[] entries, boolean eof) { + assert state == STATE_PRE; + if (error != null) { + error(error); + } + else { + if (entries != null && tmp_path == null) { + for (DirEntry e : entries) { + if (e.filename.equals("tmp") || e.filename.equalsIgnoreCase("temp")) { + tmp_path = root + "/" + e.filename; + break; + } + } + } + if (eof) { + if (tmp_path == null) { + error(new Exception("File system test filed: cannot find temporary directory")); + return; + } + files.close(handle, this); + } + else { + files.readdir(handle, this); + } + } + } + + public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) { + if (error != null) { + error(error); + } + else if (state == STATE_READ) { + if (attrs.size != data.length) { + error(new Exception("Invalid FileSysrem.fstat responce: wrong file size")); + } + else { + files.close(handle, this); + } + } + else { + file_name = tmp_path + "/tcf-test-" + (file_count++) + ".tmp"; + files.open(file_name, IFileSystem.O_CREAT | IFileSystem.O_TRUNC | IFileSystem.O_WRITE, null, this); + } + } + + public void doneOpen(IToken token, FileSystemException error, final IFileHandle handle) { + if (error != null) { + error(error); + } + else { + this.handle = handle; + if (state == STATE_READ) { + files.read(handle, 0, data.length + 1, this); + } + else if (state == STATE_WRITE) { + new Random().nextBytes(data); + files.write(handle, 0, data, 0, data.length, this); + } + else if (state == STATE_INP) { + Thread thread = new Thread() { + public void run() { + try { + InputStream inp = new TCFFileInputStream(handle); + int i = 0; + for (;;) { + int ch = inp.read(); + if (ch < 0) break; + int dt = data[i % data.length] & 0xff; + if (ch != dt) { + error(new Exception("Invalid TCFFileInputStream.read responce: wrong data at offset " + i + + ", expected " + dt + ", actual " + ch)); + } + i++; + } + if (i != data.length * 16) { + error(new Exception("Invalid TCFFileInputStream.read responce: wrong file length: " + + "expected " + data.length + ", actual " + i)); + } + inp.close(); + Protocol.invokeLater(new Runnable() { + public void run() { + state = STATE_EXIT; + files.rename(file_name, file_name + ".rnm", TestFileSystem.this); + } + }); + } + catch (Throwable x) { + error(x); + } + } + private void error(final Throwable x) { + Protocol.invokeLater(new Runnable() { + public void run() { + TestFileSystem.this.error(x); + } + }); + } + }; + thread.setName("TCF FileSystem Test"); + thread.start(); + } + else if (state == STATE_OUT) { + new Random().nextBytes(data); + Thread thread = new Thread() { + public void run() { + try { + OutputStream out = new TCFFileOutputStream(handle); + for (int i = 0; i < data.length * 16; i++) { + out.write(data[i % data.length] & 0xff); + } + out.close(); + Protocol.invokeLater(new Runnable() { + public void run() { + state = STATE_INP; + files.open(file_name, IFileSystem.O_READ, null, TestFileSystem.this); + } + }); + } + catch (Throwable x) { + error(x); + } + } + private void error(final Throwable x) { + Protocol.invokeLater(new Runnable() { + public void run() { + TestFileSystem.this.error(x); + } + }); + } + }; + thread.setName("TCF FileSystem Test"); + thread.start(); + } + else { + assert state == STATE_PRE; + files.readdir(handle, this); + } + } + } + + public void doneWrite(IToken token, FileSystemException error) { + if (error != null) { + error(error); + } + else { + files.close(handle, this); + } + } + + public void doneRead(IToken token, FileSystemException error, byte[] data, boolean eof) { + if (error != null) { + error(error); + } + else if (!eof) { + error(new Exception("Invalid FileSysrem.read responce: EOF expected")); + } + else if (data.length != this.data.length) { + error(new Exception("Invalid FileSysrem.read responce: wrong data array size")); + } + else { + for (int i = 0; i < data.length; i++) { + if (data[i] != this.data[i]) { + error(new Exception("Invalid FileSysrem.read responce: wrong data at offset " + i + + ", expected " + this.data[i] + ", actual " + data[i])); + return; + } + } + files.fstat(handle, this); + } + } + + public void doneClose(IToken token, FileSystemException error) { + if (error != null) { + error(error); + } + else { + handle = null; + if (state == STATE_PRE) { + files.realpath(tmp_path, this); + } + else if (state == STATE_WRITE) { + state = STATE_READ; + files.open(file_name, IFileSystem.O_READ, null, this); + } + else if (state == STATE_READ) { + state = STATE_OUT; + files.open(file_name, IFileSystem.O_WRITE, null, this); + } + else { + assert false; + } + } + } + + public void doneRename(IToken token, FileSystemException error) { + assert state == STATE_EXIT; + if (error != null) { + error(error); + } + else { + files.realpath(file_name + ".rnm", this); + } + } + + public void doneRealPath(IToken token, FileSystemException error, String path) { + if (error != null) { + error(error); + } + else if (state == STATE_PRE) { + state = STATE_WRITE; + tmp_path = path; + files.stat(tmp_path, this); + } + else if (!path.equals(file_name + ".rnm")) { + error(new Exception("Invalid FileSysrem.realpath responce: " + path)); + } + else { + files.remove(file_name + ".rnm", this); + } + } + + public void doneRemove(IToken token, FileSystemException error) { + assert state == STATE_EXIT; + if (error != null) { + error(error); + } + else { + done(this); + } + } + + private void error(Throwable x) { + if (active_tests.get(this) == null) return; + errors.add(x); + done(this); + } + } + + private void done(Test test) { + assert active_tests.get(test) != null; + active_tests.remove(test); + listener.progress(null, ++count_done, count_total); + if (active_tests.isEmpty()) runNextTest(); + } + + private void runNextTest() { + while (active_tests.isEmpty()) { + if (canceled || errors.size() > 0 || pending_tests.size() == 0) { + for (IChannel channel : channels) { + if (channel != null && channel.getState() != IChannel.STATE_CLOSED) { + if (errors.isEmpty()) channel.close(); + else channel.terminate(new CancelException()); + } + } + return; + } + pending_tests.removeFirst().run(); + } + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFTabGroup.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFTabGroup.java new file mode 100644 index 000000000..8d06cafd5 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TCFTabGroup.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.launch; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.EnvironmentTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.sourcelookup.SourceLookupTab; + +/** + * Launch configuration dialog tab group for Target Communication Framework + */ +public class TCFTabGroup extends AbstractLaunchConfigurationTabGroup { + + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + setTabs(new ILaunchConfigurationTab[] { + new TCFMainTab(), + new TCFArgumentsTab(), + new EnvironmentTab(), + new SourceLookupTab(), + new CommonTab() + }); + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TestErrorsDialog.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TestErrorsDialog.java new file mode 100644 index 000000000..312e85c9a --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/launch/TestErrorsDialog.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.launch; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collection; +import java.util.Iterator; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +class TestErrorsDialog extends Dialog { + + private final int SIZING_TEXT_WIDTH = 600; + private final int SIZING_TEXT_HEIGHT = 400; + + private Collection<Throwable> errors; + private Image image; + private Text text; + + TestErrorsDialog(Shell parent, Image image, Collection<Throwable> errors) { + super(parent); + this.image = image; + this.errors = errors; + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText("Connection Diagnostic errors"); + shell.setImage(image); + } + + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.OK_ID, "&OK", true); + } + + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite)super.createDialogArea(parent); + composite.setSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + + Label label = new Label(composite, SWT.WRAP); + label.setFont(JFaceResources.getFontRegistry().get(JFaceResources.BANNER_FONT)); + label.setText("Connection diagnostics ended with errors:"); + + text = new Text(composite, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); + text.setFont(JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT)); + text.setEditable(false); + text.setText(createText()); + GridData data = new GridData(GridData.FILL_BOTH); + data.widthHint = SIZING_TEXT_WIDTH; + data.heightHint = SIZING_TEXT_HEIGHT; + text.setLayoutData(data); + + return composite; + } + + private String createText() { + StringWriter buf = new StringWriter(); + PrintWriter pwr = new PrintWriter(buf); + for (Iterator<Throwable> i = errors.iterator(); i.hasNext();) { + i.next().printStackTrace(pwr); + pwr.println(); + } + pwr.flush(); + return buf.toString(); + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildren.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildren.java new file mode 100644 index 000000000..fafd5e541 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildren.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import java.util.HashMap; +import java.util.Map; + +public class TCFChildren { + + final TCFNode node; + final Map<String,TCFNode> children = new HashMap<String,TCFNode>(); + final Map<String,TCFNode> children_next = new HashMap<String,TCFNode>(); + + protected boolean valid; + + TCFChildren(TCFNode node) { + this.node = node; + } + + void dispose() { + TCFNode arr[] = children.values().toArray(new TCFNode[children.size()]); + for (int i = 0; i < arr.length; i++) arr[i].dispose(); + assert children.isEmpty(); + } + + void dispose(String id) { + children.remove(id); + } + + void doneValidate() { + valid = true; + TCFNode[] a = children.values().toArray(new TCFNode[children.size()]); + for (TCFNode n : a) { + if (children_next.get(n.id) != n) n.dispose(); + } + for (TCFNode n : children_next.values()) { + if (children.get(n.id) == null) { + children.put(n.id, n); + n.model.addNode(n.id, n); + } + assert children.get(n.id) == n; + } + assert children.size() == children_next.size(); + } + + boolean validate(TCFRunnable done) { + doneValidate(); + return true; + } + + void invalidate() { + children_next.clear(); + TCFNode[] a = children.values().toArray(new TCFNode[children.size()]); + for (int i = 0; i < a.length; i++) a[i].invalidateNode(TCFNode.CF_ALL); + valid = false; + } + + int size() { + return children.size(); + } + + TCFNode[] toArray() { + return children.values().toArray(new TCFNode[children.size()]); + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenExecContext.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenExecContext.java new file mode 100644 index 000000000..95229f778 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenExecContext.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IMemory; +import com.windriver.tcf.api.services.IRunControl; + +public class TCFChildrenExecContext extends TCFChildren { + + private boolean mem_valid; + private boolean run_valid; + + TCFChildrenExecContext(TCFNode node) { + super(node); + } + + @Override + boolean validate(TCFRunnable done) { + if (!mem_valid && !validateMemoryChildren(done)) return false; + if (!run_valid && !validateRunControlChildren(done)) return false; + doneValidate(); + return true; + } + + @Override + void invalidate() { + mem_valid = false; + run_valid = false; + super.invalidate(); + } + + private boolean validateMemoryChildren(TCFRunnable done) { + assert node.data_command == null; + IMemory mem = node.model.getLaunch().getService(IMemory.class); + if (mem == null) { + mem_valid = true; + return true; + } + if (done != null) node.wait_list.add(done); + node.data_command = mem.getChildren(node.id, new IMemory.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + if (node.data_command != token) return; + node.data_command = null; + if (error != null) { + node.node_error = error; + } + else { + for (int i = 0; i < contexts.length; i++) { + String id = contexts[i]; + TCFNode n = node.model.getNode(id); + if (n == null) n = new TCFNodeExecContext(node, id); + children_next.put(id, n); + } + } + mem_valid = true; + node.validateNode(null); + } + }); + return false; + } + + private boolean validateRunControlChildren(TCFRunnable done) { + assert node.data_command == null; + IRunControl run = node.model.getLaunch().getService(IRunControl.class); + if (run == null) { + run_valid = true; + return true; + } + if (done != null) node.wait_list.add(done); + node.data_command = run.getChildren(node.id, new IRunControl.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + if (node.data_command != token) return; + node.data_command = null; + if (error != null) { + node.node_error = error; + } + else { + for (String id : contexts) { + TCFNode n = node.model.getNode(id); + if (n == null) n = new TCFNodeExecContext(node, id); + children_next.put(id, n); + } + } + run_valid = true; + node.validateNode(null); + } + }); + return false; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenRegisters.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenRegisters.java new file mode 100644 index 000000000..ec76afab7 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenRegisters.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IRegisters; + +public class TCFChildrenRegisters extends TCFChildren { + + TCFChildrenRegisters(TCFNode node) { + super(node); + } + + @Override + boolean validate(TCFRunnable done) { + children_next.clear(); + String addr = node.getAddress(); + if (addr == null) { + doneValidate(); + return true; + } + IRegisters regs = node.model.getLaunch().getService(IRegisters.class); + if (regs == null) { + doneValidate(); + return true; + } + assert node.data_command == null; + if (done != null) node.wait_list.add(done); + node.data_command = regs.getChildren(node.id, new IRegisters.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + if (node.data_command != token) return; + node.data_command = null; + if (error != null) { + node.node_error = error; + } + else { + for (String id : contexts) { + TCFNode n = node.model.getNode(id); + if (n == null) n = new TCFNodeRegister(node, id); + children_next.put(id, n); + } + } + doneValidate(); + node.validateNode(null); + } + }); + return false; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenStackTrace.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenStackTrace.java new file mode 100644 index 000000000..8bcf2e757 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFChildrenStackTrace.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IStackTrace; + +public class TCFChildrenStackTrace extends TCFChildren { + + private final TCFChildren children_regs; + + TCFChildrenStackTrace(TCFNode node, TCFChildren children_regs) { + super(node); + this.children_regs = children_regs; + } + + @Override + boolean validate(TCFRunnable done) { + children_next.clear(); + String addr = node.getAddress(); + if (addr == null) { + doneValidate(); + return true; + } + String nm = node.id + "-TF"; + TCFNode n = children.get(nm); + if (n == null) n = new TCFNodeStackFrame(node, nm, children_regs); + children_next.put(n.id, n); + IStackTrace st = node.model.getLaunch().getService(IStackTrace.class); + if (st == null) { + doneValidate(); + return true; + } + assert node.data_command == null; + if (done != null) node.wait_list.add(done); + node.data_command = st.getChildren(node.id, new IStackTrace.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + if (node.data_command != token) return; + node.data_command = null; + if (error != null) { + node.node_error = error; + } + else { + int cnt = contexts.length; + for (String id : contexts) { + TCFNode n = node.model.getNode(id); + if (n == null || ((TCFNodeStackFrame)n).getFrameNo() != cnt) { + n = new TCFNodeStackFrame(node, id, cnt); + } + assert ((TCFNodeStackFrame)n).getFrameNo() == cnt; + assert n.id.equals(id); + children_next.put(id, n); + cnt--; + } + } + doneValidate(); + node.validateNode(null); + } + }); + return false; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFColumnPresentationRegister.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFColumnPresentationRegister.java new file mode 100644 index 000000000..8881fcb20 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFColumnPresentationRegister.java @@ -0,0 +1,81 @@ +package com.windriver.debug.tcf.ui.model; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; + +public class TCFColumnPresentationRegister implements IColumnPresentation { + + public static final String PRESENTATION_ID = "Registers"; + + private static String[] cols_all = { + TCFNodeRegister.COL_NAME, + TCFNodeRegister.COL_HEX_VALUE, + TCFNodeRegister.COL_DEC_VALUE, + TCFNodeRegister.COL_DESCRIPTION, + TCFNodeRegister.COL_READBLE, + TCFNodeRegister.COL_READ_ONCE, + TCFNodeRegister.COL_WRITEABLE, + TCFNodeRegister.COL_WRITE_ONCE, + TCFNodeRegister.COL_SIDE_EFFECTS, + TCFNodeRegister.COL_VOLATILE, + TCFNodeRegister.COL_FLOAT, + TCFNodeRegister.COL_MNEMONIC + }; + + private static String[] headers = { + "Name", + "Hex", + "Decimal", + "Description", + "Readable", + "Read Once", + "Writable", + "Write Once", + "Side Effects", + "Volatile", + "Float", + "Mnemonic" + }; + + private static String[] cols_ini = { + TCFNodeRegister.COL_NAME, + TCFNodeRegister.COL_HEX_VALUE, + TCFNodeRegister.COL_DEC_VALUE, + TCFNodeRegister.COL_DESCRIPTION, + TCFNodeRegister.COL_MNEMONIC + }; + + public void dispose() { + } + + public String[] getAvailableColumns() { + return cols_all; + } + + public String getHeader(String id) { + for (int i = 0; i < cols_all.length; i++) { + if (id.equals(cols_all[i])) return headers[i]; + } + return null; + } + + public String getId() { + return PRESENTATION_ID; + } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + public String[] getInitialColumns() { + return cols_ini; + } + + public void init(IPresentationContext context) { + } + + public boolean isOptional() { + return false; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModel.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModel.java new file mode 100644 index 000000000..f4826d2c3 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModel.java @@ -0,0 +1,351 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.debug.core.commands.IDisconnectHandler; +import org.eclipse.debug.core.commands.IResumeHandler; +import org.eclipse.debug.core.commands.IStepIntoHandler; +import org.eclipse.debug.core.commands.IStepOverHandler; +import org.eclipse.debug.core.commands.IStepReturnHandler; +import org.eclipse.debug.core.commands.ISuspendHandler; +import org.eclipse.debug.core.commands.ITerminateHandler; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.swt.widgets.Display; + +import com.windriver.debug.tcf.core.model.TCFLaunch; +import com.windriver.debug.tcf.ui.commands.DisconnectCommand; +import com.windriver.debug.tcf.ui.commands.ResumeCommand; +import com.windriver.debug.tcf.ui.commands.StepIntoCommand; +import com.windriver.debug.tcf.ui.commands.StepOverCommand; +import com.windriver.debug.tcf.ui.commands.StepReturnCommand; +import com.windriver.debug.tcf.ui.commands.SuspendCommand; +import com.windriver.debug.tcf.ui.commands.TerminateCommand; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IMemory; +import com.windriver.tcf.api.services.IRunControl; + +public class TCFModel implements IElementContentProvider, IElementLabelProvider, + IModelProxyFactory, IColumnPresentationFactory { + + private final Display display; + private final TCFLaunch launch; + private final TCFNode launch_node; + private final Map<IPresentationContext,TCFModelProxy> model_proxies = + new HashMap<IPresentationContext,TCFModelProxy>(); + private final Map<String,TCFNode> id2node = new HashMap<String,TCFNode>(); + private final Map<TCFNode,ModelDelta> deltas = new HashMap<TCFNode,ModelDelta>(); + @SuppressWarnings("unchecked") + private final Map<Class,Object> commands = new HashMap<Class,Object>(); + + private final IMemory.MemoryListener mem_listener = new IMemory.MemoryListener() { + + public void contextAdded(IMemory.MemoryContext[] contexts) { + for (int i = 0; i < contexts.length; i++) { + TCFNode node = getNode(contexts[i].getParentID()); + if (node != null) node.onContextAdded(contexts[i]); + } + fireModelChanged(); + } + + public void contextChanged(IMemory.MemoryContext[] contexts) { + for (int i = 0; i < contexts.length; i++) { + TCFNode node = getNode(contexts[i].getID()); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextChanged(contexts[i]); + } + } + fireModelChanged(); + } + + public void contextRemoved(String[] context_ids) { + for (int i = 0; i < context_ids.length; i++) { + TCFNode node = getNode(context_ids[i]); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextRemoved(); + } + } + fireModelChanged(); + } + + public void memoryChanged(String context_id, Number[] addr, long[] size) { + TCFNode node = getNode(context_id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onMemoryChanged(addr, size); + } + fireModelChanged(); + } + }; + + private final IRunControl.RunControlListener run_listener = new IRunControl.RunControlListener() { + + public void containerResumed(String[] context_ids) { + for (int i = 0; i < context_ids.length; i++) { + TCFNode node = getNode(context_ids[i]); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContainerResumed(); + } + } + fireModelChanged(); + } + + public void containerSuspended(String context, String pc, String reason, + Map<String,Object> params, String[] suspended_ids) { + for (int i = 0; i < suspended_ids.length; i++) { + TCFNode node = getNode(suspended_ids[i]); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContainerSuspended(); + } + } + TCFNode node = getNode(context); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params); + } + fireModelChanged(); + } + + public void contextAdded(IRunControl.RunControlContext[] contexts) { + for (int i = 0; i < contexts.length; i++) { + TCFNode node = getNode(contexts[i].getParentID()); + if (node != null) node.onContextAdded(contexts[i]); + } + fireModelChanged(); + } + + public void contextChanged(IRunControl.RunControlContext[] contexts) { + for (int i = 0; i < contexts.length; i++) { + TCFNode node = getNode(contexts[i].getID()); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextChanged(contexts[i]); + } + } + fireModelChanged(); + } + + public void contextException(String context, String msg) { + TCFNode node = getNode(context); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextException(msg); + } + fireModelChanged(); + } + + public void contextRemoved(String[] context_ids) { + for (int i = 0; i < context_ids.length; i++) { + TCFNode node = getNode(context_ids[i]); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextRemoved(); + } + } + fireModelChanged(); + } + + public void contextResumed(String context) { + TCFNode node = getNode(context); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextResumed(); + } + fireModelChanged(); + } + + public void contextSuspended(String context, String pc, String reason, Map<String,Object> params) { + TCFNode node = getNode(context); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params); + } + fireModelChanged(); + } + }; + + TCFModel(Display display, TCFLaunch launch) { + this.display = display; + this.launch = launch; + launch_node = new TCFNodeLaunch(TCFModel.this); + commands.put(ISuspendHandler.class, new SuspendCommand(this)); + commands.put(IResumeHandler.class, new ResumeCommand(this)); + commands.put(ITerminateHandler.class, new TerminateCommand(this)); + commands.put(IDisconnectHandler.class, new DisconnectCommand(this)); + commands.put(IStepIntoHandler.class, new StepIntoCommand(this)); + commands.put(IStepOverHandler.class, new StepOverCommand(this)); + commands.put(IStepReturnHandler.class, new StepReturnCommand(this)); + } + + @SuppressWarnings("unchecked") + public Object getCommand(Class c) { + Object o = commands.get(c); + assert o == null || c.isInstance(o); + return o; + } + + void onConnected() { + assert Protocol.isDispatchThread(); + IMemory mem = launch.getService(IMemory.class); + if (mem != null) mem.addListener(mem_listener); + IRunControl run = launch.getService(IRunControl.class); + if (run != null) run.addListener(run_listener); + launch_node.invalidateNode(); + launch_node.makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + fireModelChanged(); + } + + void onDisconnected() { + assert Protocol.isDispatchThread(); + TCFNode[] a = id2node.values().toArray(new TCFNode[id2node.size()]); + for (int i = 0; i < a.length; i++) { + if (!a[i].isDisposed()) a[i].dispose(); + } + launch_node.makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + fireModelChanged(); + } + + void onProxyInstalled(final TCFModelProxy p) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + model_proxies.put(p.getPresentationContext(), p); + } + }); + } + + void onProxyDisposed(final TCFModelProxy p) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + model_proxies.remove(p.getPresentationContext()); + } + }); + } + + void launchChanged() { + launch_node.makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + fireModelChanged(); + } + + void dispose() { + } + + void addNode(String id, TCFNode node) { + assert id != null; + assert Protocol.isDispatchThread(); + assert id2node.get(id) == null; + assert !node.isDisposed(); + id2node.put(id, node); + } + + void removeNode(String id) { + assert id != null; + assert Protocol.isDispatchThread(); + id2node.remove(id); + } + + ModelDelta getDelta(TCFNode node) { + return deltas.get(node); + } + + void addDelta(TCFNode node, ModelDelta delta) { + assert deltas.get(node) == null; + deltas.put(node, delta); + } + + void fireModelChanged() { + assert Protocol.isDispatchThread(); + ModelDelta delta = deltas.get(launch_node); + assert (delta == null) == deltas.isEmpty(); + if (delta != null) { + deltas.clear(); + IModelDelta top = delta.getParentDelta(); + for (TCFModelProxy p : model_proxies.values()) p.fireModelChanged(top); + } + } + + public Display getDisplay() { + return display; + } + + public TCFLaunch getLaunch() { + return launch; + } + + public TCFNode getRootNode() { + return launch_node; + } + + public TCFNode getNode(String id) { + if (id == null) return null; + if (id.equals("")) return launch_node; + assert Protocol.isDispatchThread(); + return id2node.get(id); + } + + public void update(IChildrenCountUpdate[] updates) { + for (int i = 0; i < updates.length; i++) { + Object o = updates[i].getElement(); + if (o instanceof TCFLaunch) launch_node.update(updates[i]); + else ((TCFNode)o).update(updates[i]); + } + } + + public void update(IChildrenUpdate[] updates) { + for (int i = 0; i < updates.length; i++) { + Object o = updates[i].getElement(); + if (o instanceof TCFLaunch) launch_node.update(updates[i]); + else ((TCFNode)o).update(updates[i]); + } + } + + public void update(IHasChildrenUpdate[] updates) { + for (int i = 0; i < updates.length; i++) { + Object o = updates[i].getElement(); + if (o instanceof TCFLaunch) launch_node.update(updates[i]); + else ((TCFNode)o).update(updates[i]); + } + } + + public void update(ILabelUpdate[] updates) { + for (int i = 0; i < updates.length; i++) { + Object o = updates[i].getElement(); + assert o != launch_node; + if (o instanceof TCFLaunch) launch_node.update(updates[i]); + else ((TCFNode)o).update(updates[i]); + } + } + + public IModelProxy createModelProxy(Object element, IPresentationContext context) { + return new TCFModelProxy(this); + } + + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + String id = getColumnPresentationId(context, element); + if (id == null) return null; + if (id.equals(TCFColumnPresentationRegister.PRESENTATION_ID)) return new TCFColumnPresentationRegister(); + return null; + } + + public String getColumnPresentationId(IPresentationContext context, Object element) { + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(context.getId())) { + return TCFColumnPresentationRegister.PRESENTATION_ID; + } + return null; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelManager.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelManager.java new file mode 100644 index 000000000..cfb9c9688 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelManager.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener; +import org.eclipse.swt.widgets.Display; + +import com.windriver.debug.tcf.core.model.TCFLaunch; +import com.windriver.tcf.api.protocol.Protocol; + +public class TCFModelManager { + + private final Display display; + private final Map<TCFLaunch,TCFModel> models = new HashMap<TCFLaunch,TCFModel>(); + + private final TCFLaunch.Listener tcf_launch_listener = new TCFLaunch.Listener() { + + public void onConnected(TCFLaunch launch) { + TCFModel model = models.get(launch); + if (model != null) model.onConnected(); + } + + public void onDisconnected(TCFLaunch launch) { + TCFModel model = models.get(launch); + if (model != null) model.onDisconnected(); + } + }; + + private final ILaunchesListener debug_launch_listener = new ILaunchesListener() { + + public void launchesAdded(final ILaunch[] launches) { + } + + public void launchesChanged(final ILaunch[] launches) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + for (int i = 0; i < launches.length; i++) { + if (launches[i] instanceof TCFLaunch) { + TCFModel model = models.get(launches[i]); + if (model != null) model.launchChanged(); + } + } + } + }); + } + + public void launchesRemoved(final ILaunch[] launches) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + for (int i = 0; i < launches.length; i++) { + if (launches[i] instanceof TCFLaunch) { + TCFModel model = models.remove(launches[i]); + if (model != null) model.dispose(); + } + } + } + }); + } + }; + + public TCFModelManager() { + display = Display.getDefault(); + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(debug_launch_listener); + Protocol.invokeAndWait(new Runnable() { + public void run() { + TCFLaunch.addListener(tcf_launch_listener); + } + }); + } + + public void dispose() { + DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(debug_launch_listener); + Protocol.invokeAndWait(new Runnable() { + public void run() { + TCFLaunch.removeListener(tcf_launch_listener); + for (Iterator<TCFModel> i = models.values().iterator(); i.hasNext();) { + TCFModel model = i.next(); + model.dispose(); + i.remove(); + } + assert models.isEmpty(); + } + }); + } + + public TCFModel getModel(TCFLaunch launch) { + TCFModel model = models.get(launch); + if (model == null) { + model = new TCFModel(display, launch); + models.put(launch, model); + if (launch.getChannel() != null) tcf_launch_listener.onConnected(launch); + } + return model; + } + + public TCFNode getRootNode(TCFLaunch launch) { + return getModel(launch).getRootNode(); + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelPresentation.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelPresentation.java new file mode 100644 index 000000000..07e358631 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelPresentation.java @@ -0,0 +1,247 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugModelPresentation; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.debug.ui.IValueDetailListener; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWindowListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +import com.windriver.debug.tcf.core.model.ITCFBreakpointListener; +import com.windriver.debug.tcf.core.model.TCFBreakpoint; +import com.windriver.debug.tcf.core.model.TCFBreakpointsStatus; +import com.windriver.debug.tcf.core.model.TCFLaunch; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IBreakpoints; + +public class TCFModelPresentation implements IDebugModelPresentation { + + private final Collection<ILabelProviderListener> listeners = new HashSet<ILabelProviderListener>(); + + private IWorkbenchWindow active_window; + private TCFLaunch launch_selection; + + private final TCFLaunch.Listener launch_listener = new TCFLaunch.Listener() { + + public void onConnected(TCFLaunch launch) { + updateLaunchSelection(); + } + + public void onDisconnected(TCFLaunch launch) { + updateLaunchSelection(); + } + }; + + private final ISelectionListener selection_listener = new ISelectionListener() { + + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + updateLaunchSelection(); + } + }; + + private final IWindowListener window_listener = new IWindowListener() { + + public void windowActivated(IWorkbenchWindow window) { + if (active_window != null) { + active_window.getSelectionService().removeSelectionListener( + IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); + active_window = null; + } + window.getSelectionService().addSelectionListener( + IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); + active_window = window; + updateLaunchSelection(); + } + + public void windowClosed(IWorkbenchWindow window) { + if (window == active_window) { + active_window.getSelectionService().removeSelectionListener( + IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); + active_window = null; + } + } + + public void windowDeactivated(IWorkbenchWindow window) { + } + + public void windowOpened(IWorkbenchWindow window) { + } + }; + + private final ITCFBreakpointListener breakpoint_status_listener = new ITCFBreakpointListener() { + + public void breakpointStatusChanged(String id) { + refreshBreakpointView(); + } + }; + + public TCFModelPresentation() { + Protocol.invokeAndWait(new Runnable() { + public void run() { + TCFLaunch.addListener(launch_listener); + } + }); + PlatformUI.getWorkbench().addWindowListener(window_listener); + IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (w != null) window_listener.windowActivated(w); + } + + private void updateLaunchSelection() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + TCFLaunch launch = null; + IAdaptable adaptable = DebugUITools.getDebugContext(); + if (adaptable != null) { + ILaunch x = (ILaunch)adaptable.getAdapter(ILaunch.class); + if (x instanceof TCFLaunch) { + final TCFLaunch l = (TCFLaunch)x; + final boolean[] b = new boolean[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + IChannel channel = l.getChannel(); + b[0] = channel != null && channel.getState() == IChannel.STATE_OPEN; + } + }); + if (b[0]) launch = l; + } + } + if (launch_selection != launch) { + setBreakpointStatusListener(launch_selection, launch); + launch_selection = launch; + refreshBreakpointView(); + } + } + }); + } + + private void refreshBreakpointView() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + if (active_window != null) { + final IDebugView view = (IDebugView)active_window.getActivePage().findView( + IDebugUIConstants.ID_BREAKPOINT_VIEW); + if (view != null) { + view.getViewer().refresh(); + } + } + } + }); + } + + private void setBreakpointStatusListener(final TCFLaunch prev, final TCFLaunch next) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + if (prev != null && prev.getBreakpointsStatus() != null) { + prev.getBreakpointsStatus().removeListener(breakpoint_status_listener); + } + if (next != null && next.getBreakpointsStatus() != null) { + next.getBreakpointsStatus().addListener(breakpoint_status_listener); + } + } + }); + } + + public void addListener(ILabelProviderListener listener) { + listeners.add(listener); + } + + public void removeListener(ILabelProviderListener listener) { + listeners.remove(listener); + } + + public void dispose() { + if (launch_selection != null) { + setBreakpointStatusListener(launch_selection, null); + launch_selection = null; + } + if (active_window != null) { + active_window.getSelectionService().removeSelectionListener( + IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); + active_window = null; + } + PlatformUI.getWorkbench().removeWindowListener(window_listener); + Protocol.invokeAndWait(new Runnable() { + public void run() { + TCFLaunch.removeListener(launch_listener); + } + }); + } + + public void computeDetail(IValue value, IValueDetailListener listener) { + } + + public Image getImage(Object element) { + return null; + } + + public String getText(Object element) { + if (element instanceof TCFBreakpoint) { + final TCFBreakpoint breakpoint = (TCFBreakpoint)element; + final TCFLaunch launch = launch_selection; + final String[] text = new String[1]; + text[0] = breakpoint.getText(); + if (launch != null) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + TCFBreakpointsStatus bs = launch.getBreakpointsStatus(); + if (bs != null) { + Map<String,Object> map = bs.getStatus(breakpoint); + if (map != null) { + String status = null; + String error = (String)map.get(IBreakpoints.STATUS_ERROR); + Object planted = map.get(IBreakpoints.STATUS_PLANTED); + if (error != null) status = error; + else if (planted != null) status = "Planted"; + if (status != null) text[0] += " (" + status + ")"; + } + } + } + }); + } + return text[0]; + } + return null; + } + + public void setAttribute(String attribute, Object value) { + } + + public boolean isLabelProperty(Object element, String property) { + return true; + } + + public String getEditorId(IEditorInput input, Object element) { + return null; + } + + public IEditorInput getEditorInput(Object element) { + return null; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelProxy.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelProxy.java new file mode 100644 index 000000000..dbdae8822 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelProxy.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.jface.viewers.Viewer; + +public class TCFModelProxy extends AbstractModelProxy implements IModelProxy { + + private final TCFModel model; + + TCFModelProxy(TCFModel model) { + this.model = model; + } + + public void installed(Viewer viewer) { + super.installed(viewer); + model.onProxyInstalled(this); + } + + public void dispose() { + model.onProxyDisposed(this); + super.dispose(); + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelSelectionPolicy.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelSelectionPolicy.java new file mode 100644 index 000000000..6c07354ea --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFModelSelectionPolicy.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.viewers.ISelection; + +public class TCFModelSelectionPolicy implements IModelSelectionPolicy { + + public boolean contains(ISelection selection, IPresentationContext context) { + // TODO Auto-generated method stub + return false; + } + + public boolean isSticky(ISelection selection, IPresentationContext context) { + // TODO Auto-generated method stub + return false; + } + + public boolean overrides(ISelection existing, ISelection candidate, + IPresentationContext context) { + // TODO Auto-generated method stub + return false; + } + + public ISelection replaceInvalidSelection(ISelection invalidSelection, + ISelection newSelection) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNode.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNode.java new file mode 100644 index 000000000..c213e0c2d --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNode.java @@ -0,0 +1,451 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.RGB; +import org.osgi.framework.Bundle; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IMemory; +import com.windriver.tcf.api.services.IRunControl; + +/** + * TCFNode is base class for all TCF debug model elements. + */ +public class TCFNode extends PlatformObject +implements IMemoryBlockRetrievalExtension, Comparable<TCFNode> { + + protected final String id; + protected final TCFNode parent; + protected final TCFModel model; + + protected boolean disposed; + + protected TCFNode(TCFNode parent, String id) { + assert Protocol.isDispatchThread(); + this.parent = parent; + this.id = id; + model = parent.model; + } + + protected TCFNode(TCFModel model) { + id = null; + parent = null; + this.model = model; + } + + /** + * Dispose this node. The node is removed from the model. + */ + void dispose() { + assert !disposed; + if (parent != null) parent.dispose(id); + model.removeNode(id); + disposed = true; + } + + /** + * A child node is being disposed. + * The child should be removed from this node children lists. + */ + void dispose(String id) { + } + + /** + * Check if node is disposed. + * @return true if disposed. + */ + public final boolean isDisposed() { + return disposed; + } + + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if (adapter == ILaunch.class) return model.getLaunch(); + if (adapter == IModelProxyFactory.class) return model; + if (adapter == IElementLabelProvider.class) return model; + if (adapter == IElementContentProvider.class) return model; + if (adapter == IColumnPresentationFactory.class) return model; + Object o = model.getCommand(adapter); + if (o != null) return o; + //System.err.println(adapter.getName()); + return super.getAdapter(adapter); + } + + public final TCFNode getParent() { + assert Protocol.isDispatchThread(); + return parent; + } + + public IRunControl.RunControlContext getRunContext() { + return null; + } + + public IMemory.MemoryContext getMemoryContext() { + return null; + } + + public boolean isRunning() { + return false; + } + + public boolean isSuspended() { + return false; + } + + /** + * Return address of this node. + * For executable contexts and stack frames address is current PC. + * @return + */ + public String getAddress() { + return null; + } + + /** + * Retrieve children count for a presentation context. + * @param result - children count update request. + */ + final void update(final IChildrenCountUpdate result) { + new TCFRunnable(model.getDisplay(), result) { + public void run() { + if (!disposed && model.getLaunch().getChannel() != null) { + if (!validateNode(this)) return; + getData(result); + } + else { + result.setChildCount(0); + } + result.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + /** + * Retrieve children for a presentation context. + * @param result - children update request. + */ + final void update(final IChildrenUpdate result) { + new TCFRunnable(model.getDisplay(), result) { + public void run() { + if (!disposed && model.getLaunch().getChannel() != null) { + if (!validateNode(this)) return; + getData(result); + } + result.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + /** + * Check if node has children in a presentation context. + * @param result - "has children" update request. + */ + final void update(final IHasChildrenUpdate result) { + new TCFRunnable(model.getDisplay(), result) { + public void run() { + if (!disposed && model.getLaunch().getChannel() != null) { + if (!validateNode(this)) return; + getData(result); + } + else { + result.setHasChilren(false); + } + result.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + /** + * Retrieve node label for a presentation context. + * @param result - label update request. + */ + final void update(final ILabelUpdate result) { + new TCFRunnable(model.getDisplay(), result) { + public void run() { + if (!disposed) { + if (!validateNode(this)) return; + if (node_error != null) { + result.setBackground(new RGB(255, 0, 0), 0); + result.setLabel(node_error.getClass().getName() + + ": " + node_error.getMessage(), 0); + } + else { + getData(result); + } + } + else { + result.setLabel("[Disposed]", 0); + } + result.setStatus(Status.OK_STATUS); + done(); + } + }; + } + + /** + * Retrieve children count for a presentation context. + * The node is validated before calling this method, + * so the method should return cached data. + * The method is always called on TCF dispatch thread. + * @param result - children count update request. + */ + protected void getData(IChildrenCountUpdate result) { + result.setChildCount(0); + } + + /** + * Retrieve children for a presentation context. + * The node is validated before calling this method, + * so the method should return cached data. + * The method is always called on TCF dispatch thread. + * @param result - children update request. + */ + protected void getData(IChildrenUpdate result) { + } + + /** + * Check if the node has children in a presentation context. + * The node is validated before calling this method, + * so the method should return cached data. + * The method is always called on TCF dispatch thread. + * @param result - "has children" update request. + */ + protected void getData(IHasChildrenUpdate result) { + result.setHasChilren(false); + } + + /** + * Retrieve node label for a presentation context. + * The node is validated before calling this method, + * so the method should return cached data. + * The method is always called on TCF dispatch thread. + * @param result - label update request. + */ + protected void getData(ILabelUpdate result) { + result.setImageDescriptor(getImageDescriptor(getImageName()), 0); + result.setLabel(id, 0); + } + + /** + * Create ModelDelta for changes in this node. + * @param flags - description of what has changed: CF_CONTEXT, CF_CHILDREN or CF_ALL. + * @return - ModelDelta that describes node changes. + */ + ModelDelta makeModelDelta(int flags) { + int count = -1; + //if (node_valid == CF_ALL) count = children.size(); + ModelDelta delta = model.getDelta(this); + int index = -1; + /* + if (parent.node_valid == CF_ALL) { + index = 0; + for (Iterator<TCFNode> i = parent.children.values().iterator(); i.hasNext();) { + if (i.next() == this) break; + index++; + } + } + */ + if (delta == null || delta.getChildCount() != count || delta.getIndex() != index) { + ModelDelta parent_delta = parent.makeModelDelta(IModelDelta.NO_CHANGE); + delta = parent_delta.addNode(this, index, flags, count); + model.addDelta(this, delta); + } + else { + delta.setFlags(delta.getFlags() | flags); + } + return delta; + } + + void onContextAdded(IRunControl.RunControlContext context) { + assert !disposed; + // TODO: Bug in Eclipse: IModelDelta.INSERTED fails if this is root node + invalidateNode(CF_CHILDREN); + makeModelDelta(IModelDelta.CONTENT); + } + + void onContextAdded(IMemory.MemoryContext context) { + assert !disposed; + // TODO: Bug in Eclipse: IModelDelta.INSERTED fails if this is root node + invalidateNode(CF_CHILDREN); + makeModelDelta(IModelDelta.CONTENT); + } + + /*--------------------------------------------------------------------------------------*/ + /* Node data retrieval state machine */ + + protected static final int + CF_CHILDREN = 0x0001, + CF_CONTEXT = 0x0002, + CF_ALL = CF_CHILDREN | CF_CONTEXT; + + protected int node_valid; + protected Throwable node_error; + protected IToken data_command; + protected final Collection<TCFRunnable> wait_list = new ArrayList<TCFRunnable>(); + + /** + * Invalidate the node - flush all cached data. + */ + public void invalidateNode() { + invalidateNode(CF_ALL); + } + + protected void invalidateNode(int flags) { + // flags - set of CF_* + + // cancel current data retrieval command + if (data_command != null) { + data_command.cancel(); + data_command = null; + } + + // cancel waiting monitors + if (!wait_list.isEmpty()) { + TCFRunnable[] arr = wait_list.toArray(new TCFRunnable[wait_list.size()]); + for (TCFRunnable r : arr) r.cancel(); + wait_list.clear(); + } + + if (flags == CF_ALL) { + node_error = null; + } + + node_valid &= ~flags; + } + + /** + * Validate node - retrieve and put into a cache missing data from remote peer. + * Validation is done asynchronously. + * @param done - call back, it is called when validation is done. + * @return true if the node is valid, false if validation is started. + */ + public boolean validateNode(TCFRunnable done) { + assert Protocol.isDispatchThread(); + assert (node_valid & ~CF_ALL) == 0; + if (data_command != null) { + if (done != null) wait_list.add(done); + return false; + } + else if (model.getLaunch().getChannel() == null) { + node_error = null; + node_valid = CF_ALL; + } + else { + if ((node_valid & CF_CONTEXT) == 0 && !validateContext(done)) return false; + if ((node_valid & CF_CHILDREN) == 0 && !validateChildren(done)) return false; + } + assert node_valid == CF_ALL; + if (!wait_list.isEmpty()) { + Runnable[] arr = wait_list.toArray(new Runnable[wait_list.size()]); + wait_list.clear(); + for (int i = 0; i < arr.length; i++) arr[i].run(); + } + return true; + } + + protected boolean validateContext(TCFRunnable done) { + node_valid |= CF_CONTEXT; + return true; + } + + protected boolean validateChildren(TCFRunnable done) { + node_valid |= CF_CHILDREN; + return true; + } + + /*--------------------------------------------------------------------------------------*/ + /* Memory Block Retrieval */ + + public IMemoryBlockExtension getExtendedMemoryBlock(String addr, Object ctx) throws DebugException { + assert ctx == this; + return getMemoryBlock(addr, 0); + } + + public IMemoryBlock getMemoryBlock(long addr, long length) throws DebugException { + return getMemoryBlock(Long.toString(addr), length); + } + + public boolean supportsStorageRetrieval() { + return getMemoryContext() != null; + } + + private IMemoryBlockExtension getMemoryBlock(String addr, long length) throws DebugException { + assert !Protocol.isDispatchThread(); + // TODO: MemoryBlock + return null; + } + + /*--------------------------------------------------------------------------------------*/ + /* Misc */ + + private static final Map<String,ImageDescriptor> image_cache = new HashMap<String,ImageDescriptor>(); + + static ImageDescriptor getImageDescriptor(String name) { + if (name == null) return null; + ImageDescriptor descriptor = image_cache.get(name); + if (descriptor == null) { + descriptor = ImageDescriptor.getMissingImageDescriptor(); + Bundle bundle = Platform.getBundle("org.eclipse.debug.ui"); + if (bundle != null){ + URL url = FileLocator.find(bundle, new Path(name), null); + descriptor = ImageDescriptor.createFromURL(url); + } + image_cache.put(name, descriptor); + } + return descriptor; + } + + protected String getImageName() { + return null; + } + + public int compareTo(TCFNode n) { + return id.compareTo(n.id); + } + + public String toString() { + String s = "[" + Integer.toHexString(hashCode()) + "] " + id; + if (disposed) s += ", disposed"; + return s; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeExecContext.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeExecContext.java new file mode 100644 index 000000000..996391ce0 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeExecContext.java @@ -0,0 +1,487 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import java.util.Arrays; +import java.util.Map; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.ui.IDebugUIConstants; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IMemory; +import com.windriver.tcf.api.services.IRunControl; + +public class TCFNodeExecContext extends TCFNode { + + private final TCFChildren children_exec; + private final TCFChildren children_stack; + private final TCFChildren children_regs; + + private IMemory.MemoryContext mem_context; + private IRunControl.RunControlContext run_context; + + private boolean suspended; + private String suspended_pc; + private String suspended_reason; + @SuppressWarnings("unused") + private Map<String,Object> suspended_params; + private boolean running; + private boolean terminated; + @SuppressWarnings("unused") + private String exception_msg; + + private boolean valid_mem_ctx; + private boolean valid_run_ctx; + private boolean valid_state; + + TCFNodeExecContext(TCFNode parent, String id) { + super(parent, id); + children_exec = new TCFChildrenExecContext(this); + children_regs = new TCFChildrenRegisters(this); + children_stack = new TCFChildrenStackTrace(this, children_regs); + } + + @Override + void dispose() { + children_exec.dispose(); + children_stack.dispose(); + children_regs.dispose(); + super.dispose(); + } + + @Override + void dispose(String id) { + children_exec.dispose(id); + children_stack.dispose(id); + children_regs.dispose(id); + } + + @Override + public IRunControl.RunControlContext getRunContext() { + assert Protocol.isDispatchThread(); + return run_context; + } + + @Override + public IMemory.MemoryContext getMemoryContext() { + assert Protocol.isDispatchThread(); + return mem_context; + } + + @Override + public boolean isRunning() { + assert Protocol.isDispatchThread(); + return running; + } + + @Override + public boolean isSuspended() { + assert Protocol.isDispatchThread(); + return suspended; + } + + @Override + public String getAddress() { + assert Protocol.isDispatchThread(); + return suspended_pc; + } + + @Override + protected void getData(IChildrenCountUpdate result) { + if (run_context != null && run_context.hasState()) { + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + result.setChildCount(children_regs.size()); + } + else { + result.setChildCount(children_stack.size()); + } + } + else { + result.setChildCount(children_exec.size()); + } + } + + @Override + protected void getData(IChildrenUpdate result) { + int offset = 0; + TCFNode[] arr = null; + if (run_context != null && run_context.hasState()) { + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + arr = children_regs.toArray(); + } + else { + arr = children_stack.toArray(); + } + } + else { + arr = children_exec.toArray(); + } + Arrays.sort(arr); + for (TCFNode n : arr) { + if (offset >= result.getOffset() && offset < result.getOffset() + result.getLength()) { + result.setChild(n, offset); + } + offset++; + } + } + + @Override + protected void getData(IHasChildrenUpdate result) { + if (run_context != null && run_context.hasState()) { + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + result.setHasChilren(children_regs.size() > 0); + } + else { + result.setHasChilren(children_stack.size() > 0); + } + } + else { + result.setHasChilren(children_exec.size() > 0); + } + } + + @Override + protected void getData(ILabelUpdate result) { + result.setImageDescriptor(getImageDescriptor(getImageName()), 0); + String label = id; + if (run_context != null) { + if (run_context.hasState()) { + if (running) { + label += " (Running)"; + } + else if (suspended) { + if (suspended_reason != null) { + label += " (" + suspended_reason + ")"; + } + else { + label += " (Suspended)"; + } + } + } + String file = (String)run_context.getProperties().get("File"); + if (file != null) label += " " + file; + } + result.setLabel(label, 0); + } + + @Override + ModelDelta makeModelDelta(int flags) { + if (run_context != null && run_context.isContainer()) flags |= IModelDelta.STATE; + return super.makeModelDelta(flags); + } + + @Override + void onContextAdded(IRunControl.RunControlContext context) { + assert !disposed; + if (node_valid == CF_ALL) { + String id = context.getID(); + TCFNodeExecContext n = (TCFNodeExecContext)children_exec.children.get(id); + if (n == null) { + n = new TCFNodeExecContext(this, id); + n.run_context = context; + n.valid_run_ctx = true; + children_exec.children.put(id, n); + model.addNode(id, n); + n.makeModelDelta(IModelDelta.INSERTED); + } + else { + n.run_context = context; + n.makeModelDelta(IModelDelta.STATE); + } + } + else { + invalidateNode(CF_CHILDREN); + makeModelDelta(IModelDelta.CONTENT); + } + } + + void onContextChanged(IRunControl.RunControlContext context) { + assert !disposed; + run_context = context; + invalidateNode(CF_CHILDREN); + makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + } + + @Override + void onContextAdded(IMemory.MemoryContext context) { + assert !disposed; + if (node_valid == CF_ALL) { + String id = context.getID(); + TCFNodeExecContext n = (TCFNodeExecContext)children_exec.children.get(id); + if (n == null) { + n = new TCFNodeExecContext(this, id); + n.mem_context = context; + n.valid_mem_ctx = true; + children_exec.children.put(id, n); + model.addNode(id, n); + n.makeModelDelta(IModelDelta.INSERTED); + } + else { + n.mem_context = context; + n.makeModelDelta(IModelDelta.STATE); + } + } + else { + invalidateNode(CF_CHILDREN); + makeModelDelta(IModelDelta.CONTENT); + } + } + + void onContextChanged(IMemory.MemoryContext context) { + assert !disposed; + mem_context = context; + invalidateNode(CF_CHILDREN); + makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + } + + void onContextRemoved() { + assert !disposed; + dispose(); + if (parent.node_valid == CF_ALL) { + makeModelDelta(IModelDelta.REMOVED); + } + else { + parent.invalidateNode(CF_CHILDREN); + parent.makeModelDelta(IModelDelta.CONTENT); + } + } + + void onContainerSuspended() { + assert !disposed; + if (run_context == null) return; + if (!run_context.hasState()) return; + suspended = false; + running = false; + valid_state = false; + invalidateNode(CF_CHILDREN); + suspended = true; + makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + } + + void onContainerResumed() { + assert !disposed; + if (run_context == null) return; + if (!run_context.hasState()) return; + suspended = false; + running = false; + valid_state = false; + invalidateNode(CF_CHILDREN); + suspended = false; + makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + } + + void onContextSuspended(String pc, String reason, Map<String,Object> params) { + assert !disposed; + if (run_context == null) return; + if (!run_context.hasState()) return; + invalidateNode(CF_CHILDREN); + suspended = true; + suspended_pc = pc; + suspended_reason = reason; + suspended_params = params; + running = false; + valid_state = true; + makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + } + + void onContextResumed() { + assert !disposed; + if (run_context == null) return; + if (!run_context.hasState()) return; + invalidateNode(CF_CHILDREN); + exception_msg = null; + terminated = false; + suspended = false; + suspended_pc = null; + suspended_reason = null; + suspended_params = null; + running = true; + valid_state = true; + makeModelDelta(IModelDelta.STATE | IModelDelta.CONTENT); + } + + void onContextException(String msg) { + assert !disposed; + exception_msg = msg; + makeModelDelta(IModelDelta.STATE); + } + + void onMemoryChanged(Number[] addr, long[] size) { + assert !disposed; + } + + @Override + protected void invalidateNode(int flags) { + super.invalidateNode(flags); + if ((flags & CF_CONTEXT) != 0) { + valid_mem_ctx = false; + valid_run_ctx = false; + valid_state = false; + running = false; + suspended = false; + } + if ((flags & CF_CHILDREN) != 0) { + children_exec.invalidate(); + children_stack.invalidate(); + children_regs.invalidate(); + } + } + + @Override + protected boolean validateContext(TCFRunnable done) { + if (!valid_mem_ctx && !validateMemoryContext(done)) return false; + if (!valid_run_ctx && !validateRunControlContext(done)) return false; + if (!valid_state && !validateRunControlState(done)) return false; + node_valid |= CF_CONTEXT; + return true; + } + + @Override + protected boolean validateChildren(TCFRunnable done) { + if (!children_stack.valid && !children_stack.validate(done)) return false; + if (!children_regs.valid && !children_regs.validate(done)) return false; + if (!children_exec.valid && !children_exec.validate(done)) return false; + node_valid |= CF_CHILDREN; + return true; + } + + private boolean validateMemoryContext(TCFRunnable done) { + assert data_command == null; + IMemory mem = model.getLaunch().getService(IMemory.class); + if (mem == null) { + valid_mem_ctx = true; + return true; + } + if (done != null) wait_list.add(done); + data_command = mem.getContext(id, new IMemory.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IMemory.MemoryContext context) { + if (data_command != token) return; + data_command = null; + if (error != null) { + node_error = error; + } + else { + mem_context = context; + } + valid_mem_ctx = true; + validateNode(null); + } + }); + return false; + } + + private boolean validateRunControlContext(TCFRunnable done) { + assert data_command == null; + IRunControl run = model.getLaunch().getService(IRunControl.class); + if (run == null) { + valid_run_ctx = true; + return true; + } + if (done != null) wait_list.add(done); + data_command = run.getContext(id, new IRunControl.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext context) { + if (data_command != token) return; + data_command = null; + if (error != null) { + node_error = error; + } + else { + run_context = context; + } + valid_run_ctx = true; + validateNode(null); + } + }); + return false; + } + + private boolean validateRunControlState(TCFRunnable done) { + assert data_command == null; + if (node_error != null || run_context == null || !run_context.hasState()) { + suspended = false; + suspended_pc = null; + suspended_reason = null; + suspended_params = null; + running = false; + valid_state = true; + return true; + } + if (done != null) wait_list.add(done); + data_command = run_context.getState(new IRunControl.DoneGetState() { + public void doneGetState(IToken token, Exception error, boolean suspend, String pc, String reason, Map<String,Object> params) { + if (token != data_command) return; + data_command = null; + if (error != null) { + suspended = false; + suspended_pc = null; + suspended_reason = null; + suspended_params = null; + node_error = error; + running = false; + } + else { + suspended = suspend; + if (suspend) { + suspended_pc = pc; + suspended_reason = reason; + suspended_params = params; + } + else { + suspended_pc = null; + suspended_reason = null; + suspended_params = null; + } + running = !suspend; + } + valid_state = true; + validateNode(null); + } + }); + return false; + } + + private boolean hasSuspendedChildren() { + for (TCFNode n : children_exec.children.values()) { + if (n instanceof TCFNodeExecContext) { + TCFNodeExecContext e = (TCFNodeExecContext)n; + if (e.run_context != null) { + if (e.run_context.hasState() && e.suspended) return true; + if (e.run_context.isContainer() && e.hasSuspendedChildren()) return true; + } + } + } + return false; + } + + @Override + protected String getImageName() { + if (run_context != null && run_context.hasState()) { + // Thread + if (terminated) return "icons/full/obj16/threadt_obj.gif"; + if (suspended) return "icons/full/obj16/threads_obj.gif"; + return "icons/full/obj16/thread_obj.gif"; + } + else if (run_context != null) { + // Thread container (process) + if (terminated) return "icons/full/obj16/debugtt_obj.gif"; + if (hasSuspendedChildren()) return "icons/full/obj16/debugts_obj.gif"; + return "icons/full/obj16/debugt_obj.gif"; + } + return super.getImageName(); + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeLaunch.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeLaunch.java new file mode 100644 index 000000000..b800e2cbe --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeLaunch.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import java.util.Arrays; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.swt.graphics.RGB; + +import com.windriver.debug.tcf.core.model.TCFLaunch; + +public class TCFNodeLaunch extends TCFNode { + + private final TCFChildren children; + + TCFNodeLaunch(TCFModel model) { + super(model); + children = new TCFChildrenExecContext(this); + } + + @Override + void dispose() { + children.dispose(); + super.dispose(); + } + + @Override + void dispose(String id) { + children.dispose(id); + } + + @Override + protected void getData(IChildrenCountUpdate result) { + result.setChildCount(children.size()); + } + + @Override + protected void getData(IChildrenUpdate result) { + int offset = 0; + TCFNode[] arr = children.toArray(); + Arrays.sort(arr); + for (TCFNode n : arr) { + if (offset >= result.getOffset() && offset < result.getOffset() + result.getLength()) { + result.setChild(n, offset); + } + offset++; + } + } + + @Override + protected void getData(IHasChildrenUpdate result) { + result.setHasChilren(children.size() > 0); + } + + @Override + protected void getData(ILabelUpdate result) { + result.setImageDescriptor(getImageDescriptor(getImageName()), 0); + String label = id; + TCFLaunch launch = model.getLaunch(); + String status = ""; + if (launch.isConnecting()) status = "Connecting"; + else if (launch.isDisconnected()) status = "Disconnected"; + else if (launch.isTerminated()) status = "Terminated"; + Throwable error = launch.getError(); + if (error != null) { + status += " - " + error; + result.setForeground(new RGB(255, 0, 0), 0); + } + if (status.length() > 0) status = " (" + status + ")"; + label = launch.getLaunchConfiguration().getName() + status; + result.setLabel(label, 0); + } + + @Override + ModelDelta makeModelDelta(int flags) { + int count = -1; + //if (node_valid == CF_ALL) count = children.size(); + ModelDelta delta = model.getDelta(this); + if (delta == null) { + delta = new ModelDelta(DebugPlugin.getDefault().getLaunchManager(), IModelDelta.NO_CHANGE); + delta = delta.addNode(model.getLaunch(), -1, flags, count); + model.addDelta(this, delta); + } + else { + assert delta.getChildCount() == count; + delta.setFlags(delta.getFlags() | flags); + } + return delta; + } + + @Override + protected void invalidateNode(int flags) { + super.invalidateNode(flags); + if ((flags & CF_CHILDREN) != 0) { + children.invalidate(); + } + } + + @Override + protected boolean validateChildren(TCFRunnable done) { + if (!children.valid && !children.validate(done)) return false; + node_valid |= CF_CHILDREN; + return true; + } + + @Override + protected String getImageName() { + return "icons/full/obj16/ldebug_obj.gif"; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeRegister.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeRegister.java new file mode 100644 index 000000000..9309c1bd9 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeRegister.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IRegisters; + +// TODO: hierarchical registers +public class TCFNodeRegister extends TCFNode { + + /** + * Presentation column IDs. + */ + public static final String + COL_NAME = "Name", + COL_HEX_VALUE = "HexValue", + COL_DEC_VALUE = "DecValue", + COL_DESCRIPTION = "Description", + COL_READBLE = "Readable", + COL_READ_ONCE = "ReadOnce", + COL_WRITEABLE = "Writeable", + COL_WRITE_ONCE = "WriteOnce", + COL_SIDE_EFFECTS = "SideEffects", + COL_VOLATILE = "Volatile", + COL_FLOAT = "Float", + COL_MNEMONIC = "Menimonic"; + + + private IRegisters.RegistersContext context; + private String hex_value; + private String dec_value; + private Number num_value; + private boolean valid_context; + private boolean valid_hex_value; + private boolean valid_dec_value; + + TCFNodeRegister(TCFNode parent, String id) { + super(parent, id); + } + + @Override + protected void getData(ILabelUpdate result) { + result.setImageDescriptor(getImageDescriptor(getImageName()), 0); + if (context != null) { + String[] cols = result.getColumnIds(); + if (cols == null) { + result.setLabel(context.getName() + " = " + hex_value, 0); + } + else { + for (int i = 0; i < cols.length; i++) { + String c = cols[i]; + if (c.equals(COL_NAME)) result.setLabel(context.getName(), i); + else if (c.equals(COL_HEX_VALUE)) result.setLabel(hex_value, i); + else if (c.equals(COL_DEC_VALUE)) result.setLabel(dec_value, i); + else if (c.equals(COL_DESCRIPTION)) result.setLabel(context.getDescription(), i); + else if (c.equals(COL_READBLE)) result.setLabel(bool(context.isReadable()), i); + else if (c.equals(COL_READ_ONCE)) result.setLabel(bool(context.isReadOnce()), i); + else if (c.equals(COL_WRITEABLE)) result.setLabel(bool(context.isWriteable()), i); + else if (c.equals(COL_WRITE_ONCE)) result.setLabel(bool(context.isWriteOnce()), i); + else if (c.equals(COL_SIDE_EFFECTS)) result.setLabel(bool(context.hasSideEffects()), i); + else if (c.equals(COL_VOLATILE)) result.setLabel(bool(context.isVolatile()), i); + else if (c.equals(COL_FLOAT)) result.setLabel(bool(context.isFloat()), i); + else if (c.equals(COL_MNEMONIC)) result.setLabel(getMnemonic(), i); + } + } + } + else { + result.setLabel(id, 0); + } + } + + private String bool(boolean b) { + return b ? "yes" : "no"; + } + + private String getMnemonic() { + if (num_value != null) { + IRegisters.NamedValue[] arr = context.getNamedValues(); + if (arr != null) { + if (context.isFloat()) { + for (IRegisters.NamedValue n : arr) { + if (n.getValue().doubleValue() == num_value.doubleValue()) return n.getName(); + } + } + else { + for (IRegisters.NamedValue n : arr) { + if (n.getValue().longValue() == num_value.longValue()) return n.getName(); + } + } + } + } + return ""; + } + + @Override + protected void invalidateNode(int flags) { + super.invalidateNode(flags); + if ((flags & CF_CONTEXT) != 0) { + valid_context = false; + valid_hex_value = false; + valid_dec_value = false; + hex_value = null; + dec_value = null; + num_value = null; + } + } + + @Override + protected boolean validateContext(TCFRunnable done) { + if (!valid_context && !validateRegisterContext(done)) return false; + if (!valid_hex_value && !validateRegisterHexValue(done)) return false; + if (!valid_dec_value && !validateRegisterDecValue(done)) return false; + node_valid |= CF_CONTEXT; + return true; + } + + private boolean validateRegisterContext(TCFRunnable done) { + assert data_command == null; + IRegisters regs = model.getLaunch().getService(IRegisters.class); + if (done != null) wait_list.add(done); + data_command = regs.getContext(id, new IRegisters.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) { + if (data_command != token) return; + data_command = null; + if (error != null) { + node_error = error; + } + else { + TCFNodeRegister.this.context = context; + } + valid_context = true; + validateNode(null); + } + }); + return false; + } + + private boolean validateRegisterHexValue(TCFRunnable done) { + assert data_command == null; + if (done != null) wait_list.add(done); + String[] fmts = context.getAvailableFormats(); + String fmt = null; + for (String s : fmts) { + if (s.equals(IRegisters.FORMAT_HEX)) fmt = s; + } + if (fmt == null) { + valid_hex_value = true; + return true; + } + data_command = context.get(fmt, new IRegisters.DoneGet() { + public void doneGet(IToken token, Exception error, String value) { + if (data_command != token) return; + data_command = null; + if (error != null) { + node_error = error; + } + else { + hex_value = value; + } + valid_hex_value = true; + if (!context.isFloat()) num_value = Long.valueOf(value, 16); + validateNode(null); + } + }); + return false; + } + + private boolean validateRegisterDecValue(TCFRunnable done) { + assert data_command == null; + if (done != null) wait_list.add(done); + String[] fmts = context.getAvailableFormats(); + String fmt = null; + for (String s : fmts) { + if (s.equals(IRegisters.FORMAT_DECIMAL)) fmt = s; + } + if (fmt == null) { + valid_dec_value = true; + return true; + } + data_command = context.get(fmt, new IRegisters.DoneGet() { + public void doneGet(IToken token, Exception error, String value) { + if (data_command != token) return; + data_command = null; + if (error != null) { + node_error = error; + } + else { + dec_value = value; + } + valid_dec_value = true; + if (!context.isFloat()) num_value = Long.valueOf(value, 10); + else num_value = Double.valueOf(value); + validateNode(null); + } + }); + return false; + } + + @Override + protected String getImageName() { + return "icons/full/obj16/genericregister_obj.gif"; + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeStackFrame.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeStackFrame.java new file mode 100644 index 000000000..91683da5e --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFNodeStackFrame.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import java.math.BigInteger; +import java.util.Arrays; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.ui.IDebugUIConstants; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ILineNumbers; +import com.windriver.tcf.api.services.IMemory; +import com.windriver.tcf.api.services.IRunControl; +import com.windriver.tcf.api.services.IStackTrace; +import com.windriver.tcf.api.services.ILineNumbers.CodeArea; + +public class TCFNodeStackFrame extends TCFNode { + + private IStackTrace.StackTraceContext stack_trace_context; + private ILineNumbers.CodeArea code_area; + + private final int frame_no; + private final TCFChildren children_regs; + + TCFNodeStackFrame(TCFNode parent, String id, TCFChildren children_regs) { + super(parent, id); + this.frame_no = 0; + this.children_regs = children_regs; + } + + TCFNodeStackFrame(TCFNode parent, String id, int frame_no) { + super(parent, id); + this.frame_no = frame_no; + children_regs = new TCFChildrenRegisters(this); + } + + int getFrameNo() { + return frame_no; + } + + @Override + void dispose() { + children_regs.dispose(); + super.dispose(); + } + + @Override + void dispose(String id) { + children_regs.dispose(id); + } + + @Override + public IRunControl.RunControlContext getRunContext() { + return parent.getRunContext(); + } + + @Override + public IMemory.MemoryContext getMemoryContext() { + return parent.getMemoryContext(); + } + + @Override + public boolean isRunning() { + return parent.isRunning(); + } + + @Override + public boolean isSuspended() { + return parent.isSuspended(); + } + + @Override + public String getAddress() { + assert Protocol.isDispatchThread(); + if (frame_no == 0) return parent.getAddress(); + if (stack_trace_context != null) { + return stack_trace_context.getReturnAddress().toString(); + } + return null; + } + + @Override + protected void getData(IChildrenCountUpdate result) { + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + result.setChildCount(children_regs.size()); + } + else { + result.setChildCount(0); + } + } + + @Override + protected void getData(IChildrenUpdate result) { + int offset = 0; + TCFNode[] arr = null; + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + arr = children_regs.toArray(); + } + else { + arr = null; + } + if (arr != null) { + Arrays.sort(arr); + for (TCFNode n : arr) { + if (offset >= result.getOffset() && offset < result.getOffset() + result.getLength()) { + result.setChild(n, offset); + } + offset++; + } + } + } + + @Override + protected void getData(IHasChildrenUpdate result) { + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(result.getPresentationContext().getId())) { + result.setHasChilren(children_regs.size() > 0); + } + else { + result.setHasChilren(false); + } + } + + @Override + protected void getData(ILabelUpdate result) { + result.setImageDescriptor(getImageDescriptor(getImageName()), 0); + String label = id; + Number n = null; + if (frame_no == 0 && parent.getAddress() != null) { + n = new BigInteger(parent.getAddress()); + } + else if (stack_trace_context != null) { + n = stack_trace_context.getReturnAddress(); + } + if (n == null) { + label = "..."; + } + else { + label = makeHexAddrString(n); + if (code_area != null && code_area.file != null) { + label += ": " + code_area.file + ", line " + (code_area.start_line + 1); + } + } + result.setLabel(label, 0); + } + + private String makeHexAddrString(Number n) { + BigInteger i = null; + if (n instanceof BigInteger) i = (BigInteger)n; + else i = new BigInteger(n.toString()); + String s = i.toString(16); + IMemory.MemoryContext m = getMemoryContext(); + int sz = (m != null ? m.getAddressSize() : 4) * 2; + int l = sz - s.length(); + if (l < 0) l = 0; + if (l > 16) l = 16; + return "0x0000000000000000".substring(0, 2 + l) + s; + } + + @Override + protected void invalidateNode(int flags) { + super.invalidateNode(flags); + if ((flags & CF_CHILDREN) != 0) { + children_regs.invalidate(); + } + } + + @Override + protected boolean validateContext(TCFRunnable done) { + assert data_command == null; + if (frame_no == 0) { + node_valid |= CF_CONTEXT; + return true; + } + IStackTrace st = model.getLaunch().getService(IStackTrace.class); + if (done != null) wait_list.add(done); + data_command = st.getContext(new String[]{ id }, new IStackTrace.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) { + if (data_command != token) return; + data_command = null; + if (error != null) { + node_error = error; + } + else { + stack_trace_context = context[0]; + } + BigInteger n = null; + ILineNumbers ln = model.getLaunch().getService(ILineNumbers.class); + if (node_error == null && ln != null) { + String s = getAddress(); + if (s != null) n = new BigInteger(s); + } + code_area = null; + if (n == null) { + node_valid |= CF_CONTEXT; + validateNode(null); + } + else { + BigInteger m = n.add(BigInteger.valueOf(1)); + data_command = ln.mapToSource(parent.id, n, m, new ILineNumbers.DoneMapToSource() { + public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) { + if (data_command != token) return; + data_command = null; + if (error != null) { + node_error = error; + } + else if (areas != null && areas.length > 0) { + for (ILineNumbers.CodeArea area : areas) { + if (code_area == null || area.start_line < code_area.start_line) { + code_area = area; + } + } + } + node_valid |= CF_CONTEXT; + validateNode(null); + } + }); + } + } + }); + return false; + } + + @Override + protected boolean validateChildren(TCFRunnable done) { + if (!children_regs.valid && !children_regs.validate(done)) return false; + node_valid |= CF_CHILDREN; + return true; + } + + @Override + protected String getImageName() { + if (isRunning()) return "icons/full/obj16/stckframe_running_obj.gif"; + return "icons/full/obj16/stckframe_obj.gif"; + } + + @Override + public int compareTo(TCFNode n) { + if (n instanceof TCFNodeStackFrame) { + TCFNodeStackFrame f = (TCFNodeStackFrame)n; + if (frame_no < f.frame_no) return -1; + if (frame_no > f.frame_no) return +1; + } + return id.compareTo(n.id); + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFRunnable.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFRunnable.java new file mode 100644 index 000000000..f55cb8afb --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/model/TCFRunnable.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.model; + +import org.eclipse.debug.core.IRequest; +import org.eclipse.swt.widgets.Display; + +import com.windriver.tcf.api.protocol.Protocol; + +public abstract class TCFRunnable implements Runnable { + + private final IRequest monitor; + private final Display display; + + public TCFRunnable(Display display, IRequest monitor) { + this.monitor = monitor; + this.display = display; + Protocol.invokeLater(this); + } + + public void cancel() { + display.asyncExec(new Runnable() { + public void run() { + monitor.cancel(); + monitor.done(); + } + }); + } + + public void done() { + display.asyncExec(new Runnable() { + public void run() { + monitor.done(); + } + }); + } +} diff --git a/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/trace/TraceView.java b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/trace/TraceView.java new file mode 100644 index 000000000..824346801 --- /dev/null +++ b/plugins/com.windriver.debug.tcf.ui/src/com/windriver/debug/tcf/ui/trace/TraceView.java @@ -0,0 +1,305 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.debug.tcf.ui.trace; + +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.part.ViewPart; + +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.core.AbstractChannel; + +public class TraceView extends ViewPart implements Protocol.ChannelOpenListener { + + private Composite parent; + private TabFolder tabs; + private Label no_data; + private final Map<TabItem,Page> tab2page = new HashMap<TabItem,Page>(); + + private class Page implements AbstractChannel.TraceListener { + + final AbstractChannel channel; + + private TabItem tab; + private Text text; + + private final StringBuffer bf = new StringBuffer(); + private int bf_line_cnt = 0; + private boolean closed; + + private final Thread update_thread = new Thread() { + public void run() { + synchronized (Page.this) { + while (!closed) { + if (bf_line_cnt > 0) { + Runnable r = new Runnable() { + public void run() { + String str = null; + int cnt = 0; + synchronized (Page.this) { + str = bf.toString(); + cnt = bf_line_cnt; + bf.setLength(0); + bf_line_cnt = 0; + } + if (text == null) return; + if (text.getLineCount() > 1000 - cnt) { + String s = text.getText(); + int n = 0; + int i = -1; + while (n < cnt) { + int j = s.indexOf('\n', i + 1); + if (j < 0) break; + i = j; + n++; + } + if (i >= 0) { + text.setText(s.substring(i + 1)); + } + } + text.append(str); + } + }; + getSite().getShell().getDisplay().asyncExec(r); + } + try { + Page.this.wait(1000); + } + catch (InterruptedException e) { + break; + } + } + } + } + }; + + Page(AbstractChannel channel) { + this.channel = channel; + update_thread.start(); + } + + public void dispose() { + synchronized (this) { + closed = true; + update_thread.interrupt(); + } + try { + update_thread.join(); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + tab2page.remove(tab); + tab.dispose(); + tab = null; + text = null; + if (tab2page.isEmpty()) hideTabs(); + } + + public synchronized void onChannelClosed(Throwable error) { + if (error == null) { + channel.removeTraceListener(this); + getSite().getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + dispose(); + } + }); + } + else { + bf.append("Channel terminated: " + error); + bf_line_cnt++; + } + } + + public synchronized void onMessageReceived(char type, String token, + String service, String name, byte[] data) { + try { + bf.append("Inp: "); + bf.append(type); + if (token != null) { + bf.append(' '); + bf.append(token); + } + if (service != null) { + bf.append(' '); + bf.append(service); + } + if (name != null) { + bf.append(' '); + bf.append(name); + } + if (data != null) { + int i = 0; + while (i < data.length) { + int j = i; + while (j < data.length && data[j] != 0) j++; + bf.append(' '); + bf.append(new String(data, i, j - i, "UTF8")); + if (j < data.length && data[j] == 0) j++; + i = j; + } + } + bf.append('\n'); + bf_line_cnt++; + } + catch (UnsupportedEncodingException x) { + x.printStackTrace(); + } + } + + public synchronized void onMessageSent(char type, String token, + String service, String name, byte[] data) { + try { + bf.append("Out: "); + bf.append(type); + if (token != null) { + bf.append(' '); + bf.append(token); + } + if (service != null) { + bf.append(' '); + bf.append(service); + } + if (name != null) { + bf.append(' '); + bf.append(name); + } + if (data != null) { + int i = 0; + while (i < data.length) { + int j = i; + while (j < data.length && data[j] != 0) j++; + bf.append(' '); + bf.append(new String(data, i, j - i, "UTF8")); + if (j < data.length && data[j] == 0) j++; + i = j; + } + } + bf.append('\n'); + bf_line_cnt++; + } + catch (UnsupportedEncodingException x) { + x.printStackTrace(); + } + } + } + + @Override + public void createPartControl(Composite parent) { + this.parent = parent; + Protocol.invokeAndWait(new Runnable() { + public void run() { + IChannel[] arr = Protocol.getOpenChannels(); + for (IChannel c : arr) onChannelOpen(c); + Protocol.addChannelOpenListener(TraceView.this); + } + }); + if (tab2page.size() == 0) hideTabs(); + } + + @Override + public void setFocus() { + if (tabs != null) tabs.setFocus(); + } + + @Override + public void dispose() { + final Page[] pages = tab2page.values().toArray(new Page[tab2page.size()]); + Protocol.invokeAndWait(new Runnable() { + public void run() { + Protocol.removeChannelOpenListener(TraceView.this); + for (Page p : pages) p.channel.removeTraceListener(p); + } + }); + for (Page p : pages) p.dispose(); + assert tab2page.isEmpty(); + if (tabs != null) { + tabs.dispose(); + tabs = null; + } + if (no_data != null) { + no_data.dispose(); + no_data = null; + } + super.dispose(); + } + + public void onChannelOpen(final IChannel channel) { + if (!(channel instanceof AbstractChannel)) return; + AbstractChannel c = (AbstractChannel)channel; + IPeer rp = c.getRemotePeer(); + final String name = rp.getName(); + final String host = rp.getAttributes().get(IPeer.ATTR_IP_HOST); + final String port = rp.getAttributes().get(IPeer.ATTR_IP_PORT); + final Page p = new Page(c); + c.addTraceListener(p); + getSite().getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + showTabs(); + p.tab = new TabItem(tabs, SWT.NONE); + tab2page.put(p.tab, p); + String title = name; + if (host != null) { + title += ", " + host; + if (port != null) { + title += ":" + port; + } + } + p.tab.setText(title); + p.text = new Text(tabs, SWT.H_SCROLL | SWT.V_SCROLL | + SWT.BORDER | SWT.READ_ONLY | SWT.MULTI); + p.tab.setControl(p.text); + p.text.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE)); + } + }); + } + + private void showTabs() { + boolean b = false; + if (no_data != null) { + no_data.dispose(); + no_data = null; + b = true; + } + if (tabs == null) { + tabs = new TabFolder(parent, SWT.NONE); + b = true; + } + if (b) parent.layout(); + } + + private void hideTabs() { + boolean b = false; + if (tabs != null) { + tabs.dispose(); + tabs = null; + b = true; + } + if (!parent.isDisposed()) { + if (no_data == null) { + no_data = new Label(parent, SWT.NONE); + no_data.setText("No open communication channels at this time."); + b = true; + } + if (b) parent.layout(); + } + } +} diff --git a/plugins/com.windriver.tcf.api/.classpath b/plugins/com.windriver.tcf.api/.classpath new file mode 100644 index 000000000..751c8f2e5 --- /dev/null +++ b/plugins/com.windriver.tcf.api/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/com.windriver.tcf.api/.cvsignore b/plugins/com.windriver.tcf.api/.cvsignore new file mode 100644 index 000000000..c5e82d745 --- /dev/null +++ b/plugins/com.windriver.tcf.api/.cvsignore @@ -0,0 +1 @@ +bin
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/.project b/plugins/com.windriver.tcf.api/.project new file mode 100644 index 000000000..48171f9bd --- /dev/null +++ b/plugins/com.windriver.tcf.api/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.windriver.tcf.api</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/plugins/com.windriver.tcf.api/.settings/org.eclipse.jdt.core.prefs b/plugins/com.windriver.tcf.api/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..d598b3c83 --- /dev/null +++ b/plugins/com.windriver.tcf.api/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,13 @@ +#Mon Sep 10 12:26:22 PDT 2007 +eclipse.preferences.version=1 +org.eclipse.jdt.core.builder.cleanOutputFolder=clean +org.eclipse.jdt.core.builder.duplicateResourceTask=warning +org.eclipse.jdt.core.builder.invalidClasspath=abort +org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore +org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,.svn/ +org.eclipse.jdt.core.circularClasspath=error +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 +org.eclipse.jdt.core.incompatibleJDKLevel=ignore +org.eclipse.jdt.core.incompleteClasspath=error diff --git a/plugins/com.windriver.tcf.api/META-INF/MANIFEST.MF b/plugins/com.windriver.tcf.api/META-INF/MANIFEST.MF new file mode 100644 index 000000000..87bb955c3 --- /dev/null +++ b/plugins/com.windriver.tcf.api/META-INF/MANIFEST.MF @@ -0,0 +1,14 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: com.windriver.tcf.api +Bundle-Version: 0.1.0 +Bundle-Activator: com.windriver.tcf.api.Activator +Bundle-Vendor: %providerName +Require-Bundle: org.eclipse.core.runtime +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Eclipse-LazyStart: true +Export-Package: com.windriver.tcf.api.core, + com.windriver.tcf.api.protocol, + com.windriver.tcf.api.services, + com.windriver.tcf.api.util diff --git a/plugins/com.windriver.tcf.api/about.html b/plugins/com.windriver.tcf.api/about.html new file mode 100755 index 000000000..6c5b3615b --- /dev/null +++ b/plugins/com.windriver.tcf.api/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>January 10, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/build.properties b/plugins/com.windriver.tcf.api/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/plugins/com.windriver.tcf.api/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/plugins/com.windriver.tcf.api/plugin.properties b/plugins/com.windriver.tcf.api/plugin.properties new file mode 100644 index 000000000..8c23febbe --- /dev/null +++ b/plugins/com.windriver.tcf.api/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2007 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Wind River Systems - initial implementation +############################################################################### +pluginName = Target Communication Framework (TCF) +providerName = Eclipse.org + diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/Activator.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/Activator.java new file mode 100644 index 000000000..97ead437f --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/Activator.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +import com.windriver.tcf.api.protocol.Protocol; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends Plugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.windriver.tcf.api"; + + // The shared instance + private static Activator plugin; + + public Activator() { + plugin = this; + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + Protocol.setEventQueue(new EventQueue()); + } + + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + /** + * Send error message into Eclipse log. + * @param msg - error message test + * @param err - exception + */ + public static void log(String msg, Throwable err) { + if (plugin == null || plugin.getLog() == null) { + err.printStackTrace(); + } + else { + plugin.getLog().log(new Status(IStatus.ERROR, + getDefault().getBundle().getSymbolicName(), IStatus.OK, msg, err)); + } + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/ErrorCodes.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/ErrorCodes.java new file mode 100644 index 000000000..e26cfa100 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/ErrorCodes.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api; + +public class ErrorCodes { + + public static final int + ERR_TERMINATE = 1; + +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/EventQueue.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/EventQueue.java new file mode 100644 index 000000000..fec45e8dd --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/EventQueue.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api; + +import java.util.LinkedList; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.IJobChangeListener; +import org.eclipse.core.runtime.jobs.Job; + +import com.windriver.tcf.api.protocol.IEventQueue; + +/** + * Implementation of Target Communication Framework event queue. + * This implementation is intended for Eclipse environment. + */ +class EventQueue implements IEventQueue, Runnable { + + private final boolean debug = Platform.inDebugMode(); + private final LinkedList<Runnable> queue = new LinkedList<Runnable>(); + private final Thread thread; + private boolean waiting; + private int job_cnt; + + EventQueue() { + thread = new Thread(this); + thread.setDaemon(true); + thread.setName("TCF Event Dispatch"); + thread.start(); + // Need to monitor jobs to detect congestion + Job.getJobManager().addJobChangeListener(new IJobChangeListener() { + + public void aboutToRun(IJobChangeEvent event) { + job_cnt++; + } + + public void awake(IJobChangeEvent event) { + //job_cnt++; + } + + public void done(IJobChangeEvent event) { + job_cnt--; + if (Job.getJobManager().isIdle()) job_cnt = 0; + } + + public void running(IJobChangeEvent event) { + } + + public void scheduled(IJobChangeEvent event) { + } + + public void sleeping(IJobChangeEvent event) { + //job_cnt--; + } + }); + } + + private void error(Throwable x) { + if (debug) x.printStackTrace(); + Activator.log("Unhandled excetion in TCF event dispatch", x); + } + + public void run() { + for (;;) { + try { + Runnable r = null; + synchronized (this) { + while (queue.isEmpty()) { + waiting = true; + wait(); + } + r = queue.removeFirst(); + } + r.run(); + } + catch (Throwable x) { + error(x); + } + } + } + + public synchronized void invokeLater(final Runnable r) { + queue.add(r); + if (waiting) { + waiting = false; + notifyAll(); + } + } + + public boolean isDispatchThread() { + return Thread.currentThread() == thread; + } + + public synchronized int getCongestion() { + int l0 = job_cnt / 100 - 100; + int l1 = queue.size() / 100 - 100; + if (l1 > l0) l0 = l1; + if (l0 > 100) l0 = 100; + return l0; + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractChannel.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractChannel.java new file mode 100644 index 000000000..767f8b153 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractChannel.java @@ -0,0 +1,796 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.core; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; + +import com.windriver.tcf.api.Activator; +import com.windriver.tcf.api.internal.core.Token; +import com.windriver.tcf.api.internal.core.Transport; +import com.windriver.tcf.api.internal.services.local.DiagnosticsService; +import com.windriver.tcf.api.internal.services.local.LocatorService; +import com.windriver.tcf.api.internal.services.remote.GenericProxy; +import com.windriver.tcf.api.internal.services.remote.LocatorProxy; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ILocator; + +public abstract class AbstractChannel implements IChannel { + + public interface TraceListener { + + public void onMessageReceived(char type, String token, + String service, String name, byte[] data); + + public void onMessageSent(char type, String token, + String service, String name, byte[] data); + + public void onChannelClosed(Throwable error); + } + + private static class Message { + final char type; + Token token; + String service; + String name; + byte[] data; + + boolean is_sent; + boolean is_canceled; + + Collection<TraceListener> trace; + + Message(char type) { + this.type = type; + } + + public String toString() { + try { + StringBuffer bf = new StringBuffer(); + bf.append('[');; + bf.append(type); + if (token != null) { + bf.append(' '); + bf.append(token.getID()); + } + if (service != null) { + bf.append(' '); + bf.append(service); + } + if (name != null) { + bf.append(' '); + bf.append(name); + } + if (data != null) { + int i = 0; + while (i < data.length) { + int j = i; + while (j < data.length && data[j] != 0) j++; + bf.append(' '); + bf.append(new String(data, i, j - i, "UTF8")); + if (j < data.length && data[j] == 0) j++; + i = j; + } + } + bf.append(']'); + return bf.toString(); + } + catch (Exception x) { + return x.toString(); + } + } + } + + private static IChannelListener[] listeners_array = new IChannelListener[64]; + + private final LinkedList<String> redirect_queue = new LinkedList<String>(); + private final Map<Class<?>,IService> local_service_by_class = new HashMap<Class<?>,IService>(); + private final Map<Class<?>,IService> remote_service_by_class = new HashMap<Class<?>,IService>(); + private final Map<String,IService> local_service_by_name = new HashMap<String,IService>(); + private final Map<String,IService> remote_service_by_name = new HashMap<String,IService>(); + private final LinkedList<Message> out_queue = new LinkedList<Message>(); + private final Collection<IChannelListener> channel_listeners = new ArrayList<IChannelListener>(); + private final Map<String,IChannel.IEventListener[]> event_listeners = new HashMap<String,IChannel.IEventListener[]>(); + private final Map<String,IChannel.ICommandServer> command_servers = new HashMap<String,IChannel.ICommandServer>(); + private final Map<String,Message> inp_tokens = new HashMap<String,Message>(); + private final Map<String,Message> out_tokens = new HashMap<String,Message>(); + private final Thread inp_thread; + private final Thread out_thread; + private boolean shutdown; + private int state = STATE_OPENNING; + private IToken redirect_command; + private IPeer peer; + + private static final int pending_command_limit = 10; + private int local_congestion_level = -100; + private int remote_congestion_level = -100; + private long local_congestion_time; + private int inp_queue_size = 0; + private Collection<TraceListener> trace_listeners; + + public static final int + EOS = -1, + EOM = -2; + + protected AbstractChannel(IPeer peer) { + assert Protocol.isDispatchThread(); + assert Protocol.getLocator().getPeers().get(peer.getID()) == peer; + this.peer = peer; + + addLocalService(Protocol.getLocator()); + addLocalService(new DiagnosticsService(this)); + + inp_thread = new Thread() { + + byte[] buf = new byte[1024]; + byte[] eos; + + private void error() throws IOException { + throw new IOException("Protocol syntax error"); + } + + private byte[] readBytes(int end) throws IOException { + int len = 0; + for (;;) { + int n = read(); + if (n == end) break; + if (n < 0) error(); + if (len >= buf.length) { + byte[] tmp = new byte[buf.length * 2]; + System.arraycopy(buf, 0, tmp, 0, len); + buf = tmp; + } + buf[len++] = (byte)n; + } + byte[] res = new byte[len]; + System.arraycopy(buf, 0, res, 0, len); + return res; + } + + private String readString() throws IOException { + return new String(readBytes(0), "UTF8"); + } + + public void run() { + try { + while (true) { + int n = read(); + if (n == EOM) continue; + if (n == EOS) { + eos = readBytes(EOM); + break; + } + final Message msg = new Message((char)n); + if (read() != 0) error(); + switch (msg.type) { + case 'C': + msg.token = new Token(readBytes(0)); + msg.service = readString(); + msg.name = readString(); + msg.data = readBytes(EOM); + break; + case 'R': + msg.token = new Token(readBytes(0)); + msg.data = readBytes(EOM); + break; + case 'E': + msg.service = readString(); + msg.name = readString(); + msg.data = readBytes(EOM); + break; + case 'F': + msg.data = readBytes(EOM); + break; + default: + error(); + } + long delay = 0; + synchronized (out_queue) { + inp_queue_size++; + if (inp_queue_size > 32) delay = inp_queue_size; + } + Protocol.invokeLater(new Runnable() { + public void run() { + handleInput(msg); + } + }); + if (delay > 0) sleep(delay); + } + Protocol.invokeLater(new Runnable() { + public void run() { + if (out_tokens.isEmpty()) { + close(); + } + else { + IOException x = new IOException("Connection reset by peer"); + try { + Object[] args = JSON.parseSequence(eos); + int error_code = ((Number)args[0]).intValue(); + if (error_code != 0) { + x = new IOException(Command.toErrorString(args[1])); + } + } + catch (IOException e) { + x = e; + } + terminate(x); + } + } + }); + } + catch (final Throwable x) { + Protocol.invokeLater(new Runnable() { + public void run() { + terminate(x); + } + }); + } + } + }; + + out_thread = new Thread() { + + public void run() { + try { + while (true) { + Message msg = null; + boolean last = false; + synchronized (out_queue) { + while (out_queue.isEmpty()) out_queue.wait(); + msg = out_queue.removeFirst(); + if (msg == null) break; + last = out_queue.isEmpty(); + if (msg.is_canceled) { + if (last) flush(); + continue; + } + msg.is_sent = true; + } + if (msg.trace != null) { + final Message m = msg; + Protocol.invokeLater(new Runnable() { + public void run() { + for (TraceListener l : m.trace) { + try { + l.onMessageSent(m.type, m.token == null ? null : m.token.getID(), + m.service, m.name, m.data); + } + catch (Throwable x) { + Activator.log("Exception in channel listener", x); + } + } + } + }); + } + write(msg.type); + write(0); + if (msg.token != null) { + write(msg.token.getBytes()); + write(0); + } + if (msg.service != null) { + write(msg.service.getBytes("UTF8")); + write(0); + } + if (msg.name != null) { + write(msg.name.getBytes("UTF8")); + write(0); + } + if (msg.data != null) { + write(msg.data); + } + write(EOM); + int delay = 0; + int level = remote_congestion_level; + if (level > 0) delay = level * 10; + if (last || delay > 0) flush(); + if (delay > 0) sleep(delay); + else yield(); + } + write(EOS); + flush(); + } + catch (final Throwable x) { + Protocol.invokeLater(new Runnable() { + public void run() { + terminate(x); + } + }); + } + } + }; + inp_thread.setName("TCF Channel Receiver"); + out_thread.setName("TCF Channel Transmitter"); + + try { + Object[] args = new Object[]{ local_service_by_name.keySet() }; + sendEvent(Protocol.getLocator(), "Hello", JSON.toJSONSequence(args)); + } + catch (IOException x) { + throw new Error(x); + } + } + + protected void start() { + assert Protocol.isDispatchThread(); + inp_thread.start(); + out_thread.start(); + LocatorService.channelStarted(this); + } + + public void redirect(String peer_id) { + assert Protocol.isDispatchThread(); + if (state == STATE_OPENNING) { + assert redirect_command == null; + redirect_queue.add(peer_id); + } + else { + assert state == STATE_OPEN; + state = STATE_OPENNING; + try { + onLocatorHello(new ArrayList<String>()); + } + catch (Throwable x) { + terminate(x); + } + } + } + + @SuppressWarnings("unchecked") + public void onLocatorHello(Collection<String> c) throws IOException { + if (state != STATE_OPENNING) throw new IOException("Invalid event: Locator.Hello"); + remote_service_by_class.clear(); + String pkg_name = LocatorProxy.class.getPackage().getName(); + for (Iterator<String> i = c.iterator(); i.hasNext();) { + String service_name = i.next(); + try { + Class<?> cls = Class.forName(pkg_name + "." + service_name + "Proxy"); + IService service = (IService)cls.getConstructor(IChannel.class).newInstance(this); + for (Class<?> fs : cls.getInterfaces()) { + if (fs.equals(IService.class)) continue; + if (!IService.class.isAssignableFrom(fs)) continue; + remote_service_by_class.put(fs, service); + } + assert service_name.equals(service.getName()); + remote_service_by_name.put(service_name, service); + } + catch (Exception x) { + IService service = new GenericProxy(this, service_name); + remote_service_by_name.put(service_name, service); + } + } + assert redirect_command == null; + if (redirect_queue.size() > 0) { + String id = redirect_queue.removeFirst(); + ILocator l = (ILocator)remote_service_by_class.get(ILocator.NAME); + if (l == null) throw new IOException("Peer " + peer.getID() + " has no locator service"); + peer = l.getPeers().get(id); + if (peer == null) throw new IOException("Unknown peer ID: " + id); + redirect_command = l.redirect(id, new ILocator.DoneRedirect() { + public void doneRedirect(IToken token, Exception x) { + assert redirect_command == token; + assert state == STATE_OPENNING; + redirect_command = null; + remote_congestion_level = 0; + if (x != null) terminate(x); + // Wait for next "Hello" + } + }); + } + else { + state = STATE_OPEN; + Transport.channelOpened(this); + Protocol.invokeLater(new Runnable() { + public void run() { + listeners_array = channel_listeners.toArray(listeners_array); + for (int i = 0; i < listeners_array.length && listeners_array[i] != null; i++) { + listeners_array[i].onChannelOpened(); + } + } + }); + } + } + + public final int getState() { + return state; + } + + public void addChannelListener(IChannelListener listener) { + assert Protocol.isDispatchThread(); + channel_listeners.add(listener); + } + + public void removeChannelListener(IChannelListener listener) { + assert Protocol.isDispatchThread(); + channel_listeners.remove(listener); + } + + public void addTraceListener(TraceListener listener) { + if (trace_listeners == null) { + trace_listeners = new ArrayList<TraceListener>(); + } + else { + trace_listeners = new ArrayList<TraceListener>(trace_listeners); + } + trace_listeners.add(listener); + } + + public void removeTraceListener(TraceListener listener) { + trace_listeners = new ArrayList<TraceListener>(trace_listeners); + trace_listeners.remove(listener); + if (trace_listeners.isEmpty()) trace_listeners = null; + } + + public void addEventListener(IService service, IChannel.IEventListener listener) { + assert Protocol.isDispatchThread(); + IChannel.IEventListener[] list = event_listeners.get(service.getName()); + IChannel.IEventListener[] next = new IChannel.IEventListener[list == null ? 1 : list.length + 1]; + if (list != null) System.arraycopy(list, 0, next, 0, list.length); + next[next.length - 1] = listener; + event_listeners.put(service.getName(), next); + } + + public void removeEventListener(IService service, IChannel.IEventListener listener) { + assert Protocol.isDispatchThread(); + IChannel.IEventListener[] list = event_listeners.get(service.getName()); + for (int i = 0; i < list.length; i++) { + if (list[i] == listener) { + if (list.length == 1) { + event_listeners.remove(service.getName()); + } + else { + IChannel.IEventListener[] next = new IChannel.IEventListener[list.length - 1]; + System.arraycopy(list, 0, next, 0, i - 1); + System.arraycopy(list, i + 1, next, i, next.length - i); + event_listeners.put(service.getName(), next); + } + return; + } + } + } + + public void addCommandServer(IService service, IChannel.ICommandServer listener) { + assert Protocol.isDispatchThread(); + if (command_servers.put(service.getName(), listener) != null) { + throw new Error("Only one command server per service is allowed"); + } + } + + public void removeCommandServer(IService service, IChannel.ICommandServer listener) { + assert Protocol.isDispatchThread(); + if (command_servers.remove(service.getName()) != listener) { + throw new Error("Invalid command server"); + } + } + + private void sendEndOfStream() { + if (shutdown) return; + shutdown = true; + synchronized (out_queue) { + out_queue.clear(); + out_queue.add(0, null); + out_queue.notify(); + } + } + + public void close() { + assert Protocol.isDispatchThread(); + try { + sendEndOfStream(); + out_thread.join(10000); + stop(); + inp_thread.join(10000); + terminate(null); + } + catch (Exception x) { + terminate(x); + } + } + + public void terminate(final Throwable error) { + assert Protocol.isDispatchThread(); + sendEndOfStream(); + if (state == STATE_CLOSED) return; + if (error != null) Activator.log("TCF channel terminated", error); + state = STATE_CLOSED; + Transport.channelClosed(this, error); + Protocol.invokeLater(new Runnable() { + public void run() { + if (!out_tokens.isEmpty()) { + Exception x = null; + if (error instanceof Exception) x = (Exception)error; + else if (error != null) x = new Exception(error); + else x = new IOException("Channel is closed"); + for (Message msg : out_tokens.values()) { + String s = msg.toString(); + if (s.length() > 72) s = s.substring(0, 72) + "...]"; + IOException y = new IOException("Command " + s + " aborted"); + y.initCause(x); + msg.token.getListener().terminated(msg.token, y); + } + out_tokens.clear(); + } + listeners_array = channel_listeners.toArray(listeners_array); + for (int i = 0; i < listeners_array.length && listeners_array[i] != null; i++) { + listeners_array[i].onChannelClosed(error); + } + if (trace_listeners != null) { + for (TraceListener l : trace_listeners) { + try { + l.onChannelClosed(error); + } + catch (Throwable x) { + Activator.log("Exception in channel listener", x); + } + } + } + } + }); + } + + public int getCongestion() { + assert Protocol.isDispatchThread(); + int level = out_tokens.size() * 100 / pending_command_limit - 100; + if (remote_congestion_level > level) level = remote_congestion_level; + if (level > 100) level = 100; + return level; + } + + public IPeer getLocalPeer() { + assert Protocol.isDispatchThread(); + return LocatorService.getLocalPeer(); + } + + public IPeer getRemotePeer() { + assert Protocol.isDispatchThread(); + return peer; + } + + private void addLocalService(IService service) { + for (Class<?> fs : service.getClass().getInterfaces()) { + if (fs.equals(IService.class)) continue; + if (!IService.class.isAssignableFrom(fs)) continue; + local_service_by_class.put(fs, service); + } + local_service_by_name.put(service.getName(), service); + } + + public Collection<String> getLocalServices() { + assert Protocol.isDispatchThread(); + return local_service_by_name.keySet(); + } + + public Collection<String> getRemoteServices() { + return remote_service_by_name.keySet(); + } + + @SuppressWarnings("unchecked") + public <V extends IService> V getLocalService(Class<V> cls) { + return (V)local_service_by_class.get(cls); + } + + @SuppressWarnings("unchecked") + public <V extends IService> V getRemoteService(Class<V> cls) { + return (V)remote_service_by_class.get(cls); + } + + public IService getLocalService(String service_name) { + return local_service_by_name.get(service_name); + } + + public IService getRemoteService(String service_name) { + return remote_service_by_name.get(service_name); + } + + private void addToOutQueue(Message msg) { + msg.trace = trace_listeners; + synchronized (out_queue) { + out_queue.add(msg); + out_queue.notify(); + } + } + + public IToken sendCommand(IService service, String name, byte[] args, ICommandListener listener) { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) throw new Error("Channel is closed"); + final Message msg = new Message('C'); + msg.service = service.getName(); + msg.name = name; + msg.data = args; + Token token = new Token(listener) { + public boolean cancel() { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) return false; + synchronized (out_queue) { + if (msg.is_sent) return false; + msg.is_canceled = true; + } + out_tokens.remove(msg.token.getID()); + return true; + } + }; + msg.token = token; + out_tokens.put(token.getID(), msg); + addToOutQueue(msg); + return token; + } + + public void sendResult(IToken token, byte[] results) { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) { + throw new Error("Channel is closed"); + } + Message msg = new Message('R'); + msg.data = results; + msg.token = (Token)token; + inp_tokens.remove(((Token)token).getID()); + addToOutQueue(msg); + } + + public void sendEvent(IService service, String name, byte[] args) { + assert Protocol.isDispatchThread(); + if (!(state == STATE_OPEN || state == STATE_OPENNING && service instanceof ILocator)) { + throw new Error("Channel is closed"); + } + Message msg = new Message('E'); + msg.service = service.getName(); + msg.name = name; + msg.data = args; + addToOutQueue(msg); + } + + private void handleInput(Message msg) { + assert Protocol.isDispatchThread(); + synchronized (out_queue) { + inp_queue_size--; + } + if (state == STATE_CLOSED) return; + if (trace_listeners != null) { + for (TraceListener l : trace_listeners) { + try { + l.onMessageReceived(msg.type, + msg.token != null ? msg.token.getID() : null, + msg.service, msg.name, msg.data); + } + catch (Throwable x) { + x.printStackTrace(); + } + } + } + try { + Token token = null; + IChannel.IEventListener[] list = null; + IChannel.ICommandServer cmds = null; + switch (msg.type) { + case 'C': + token = msg.token; + inp_tokens.put(token.getID(), msg); + cmds = command_servers.get(msg.service); + if (cmds != null) { + cmds.command(token, msg.name, msg.data); + } + else { + throw new IOException("Unknown command " + msg.service + "." + msg.name); + } + sendCongestionLevel(); + break; + case 'P': + token = out_tokens.get(msg.token.getID()).token; + token.getListener().progress(token, msg.data); + break; + case 'R': + token = out_tokens.remove(msg.token.getID()).token; + token.getListener().result(token, msg.data); + break; + case 'E': + list = event_listeners.get(msg.service); + if (list != null) { + for (int i = 0; i < list.length; i++) { + list[i].event(msg.name, msg.data); + } + } + sendCongestionLevel(); + break; + case 'F': + remote_congestion_level = Integer.parseInt(new String(msg.data, "UTF8")); + break; + default: + assert false; + break; + } + } + catch (Throwable x) { + terminate(x); + } + } + + private void sendCongestionLevel() throws IOException { + assert Protocol.isDispatchThread(); + if (state != STATE_OPEN) return; + int level = Protocol.getEventQueue().getCongestion(); + int n = inp_tokens.size() * 100 / pending_command_limit - 100; + if (n > level) level = n; + if (level > 100) level = 100; + if (level == local_congestion_level) return; + long time = System.currentTimeMillis(); + if (level < local_congestion_level) { + if (time - local_congestion_time < 500) return; + int i = (local_congestion_level - level) / 4; + if (i <= 0) i = 1; + local_congestion_level -= i; + } + else { + local_congestion_level = level; + } + local_congestion_time = time; + synchronized (out_queue) { + Message msg = out_queue.isEmpty() ? null : out_queue.get(0); + if (msg == null || msg.type != 'F') { + msg = new Message('F'); + out_queue.add(0, msg); + out_queue.notify(); + } + msg.data = Integer.toString(local_congestion_level).getBytes("UTF8"); + msg.trace = trace_listeners; + } + } + + /** + * Read one byte from the channel input stream. + * @return next data byte or -1 if end of stream is reached. + * @throws IOException + */ + protected abstract int read() throws IOException; + + /** + * Write one byte into the channel output stream. + * The stream can put the byte into a buffer instead of transmitting it right away. + * @param n - the data byte. + * @throws IOException + */ + protected abstract void write(int n) throws IOException; + + /** + * Flush the channel output stream. + * All buffered data should be transmitted immediately. + * @throws IOException + */ + protected abstract void flush() throws IOException; + + /** + * Stop (close) channel underlying streams. + * If a thread is blocked by read() or write(), it should be + * resumed (or interrupted). + * @throws IOException + */ + protected abstract void stop() throws IOException; + + /** + * Write array of bytes into the channel output stream. + * The stream can put bytes into a buffer instead of transmitting it right away. + * @param buf + * @throws IOException + */ + protected void write(byte[] buf) throws IOException { + assert Thread.currentThread() == out_thread; + for (int i = 0; i < buf.length; i++) write(buf[i]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractPeer.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractPeer.java new file mode 100644 index 000000000..314d9b03a --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/AbstractPeer.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.core; + +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.internal.core.ReadOnlyMap; +import com.windriver.tcf.api.internal.core.Transport; +import com.windriver.tcf.api.internal.services.local.LocatorService; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.Protocol; + +public abstract class AbstractPeer implements IPeer { + + private final Map<String, String> ro_attrs; + private final Map<String, String> rw_attrs; + + public AbstractPeer(Map<String, String> attrs) { + if (attrs != null) { + rw_attrs = new HashMap<String, String>(attrs); + } + else { + rw_attrs = new HashMap<String, String>(); + } + ro_attrs = new ReadOnlyMap<String, String>(rw_attrs); + assert getID() != null; + LocatorService.addPeer(this); + } + + protected Map<String, String> getAttributesStorage() { + assert Protocol.isDispatchThread(); + return rw_attrs; + } + + public void dispose() { + assert Protocol.isDispatchThread(); + Transport.peerDisposed(this); + LocatorService.removePeer(this); + } + + public Map<String, String> getAttributes() { + assert Protocol.isDispatchThread(); + return ro_attrs; + } + + public String getID() { + assert Protocol.isDispatchThread(); + return ro_attrs.get(ATTR_ID); + } + + public String getName() { + assert Protocol.isDispatchThread(); + return ro_attrs.get(ATTR_NAME); + } + + public String getOSName() { + assert Protocol.isDispatchThread(); + return ro_attrs.get(ATTR_OS_NAME); + } + + public String getTransportName() { + assert Protocol.isDispatchThread(); + return ro_attrs.get(ATTR_TRANSPORT_NAME); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Base64.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Base64.java new file mode 100644 index 000000000..498104fac --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Base64.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.core; + +/** + * Methods for translating Base64 encoded strings to byte arrays and back. + */ +public class Base64 { + + public static char[] toBase64(byte[] buf, int pos, int len) { + char[] out_buf = new char[4 * ((len + 2) / 3)]; + int end = pos + len; + int out_pos = 0; + while (pos < end) { + int byte0 = buf[pos++] & 0xff; + out_buf[out_pos++] = int2char[byte0 >> 2]; + if (pos == end) { + out_buf[out_pos++] = int2char[(byte0 << 4) & 0x3f]; + out_buf[out_pos++] = '='; + out_buf[out_pos++] = '='; + } + else { + int byte1 = buf[pos++] & 0xff; + out_buf[out_pos++] = int2char[(byte0 << 4) & 0x3f | (byte1 >> 4)]; + if (pos == end) { + out_buf[out_pos++] = int2char[(byte1 << 2) & 0x3f]; + out_buf[out_pos++] = '='; + } + else { + int byte2 = buf[pos++] & 0xff; + out_buf[out_pos++] = int2char[(byte1 << 2) & 0x3f | (byte2 >> 6)]; + out_buf[out_pos++] = int2char[byte2 & 0x3f]; + } + } + } + assert out_pos == out_buf.length; + return out_buf; + } + + public static void toByteArray(byte[] buf, int offs, int size, char[] inp) { + int out_pos = offs; + if (inp != null) { + int inp_len = inp.length; + if (inp_len % 4 != 0) { + throw new IllegalArgumentException( + "BASE64 string length must be a multiple of four."); + } + int out_len = inp_len / 4 * 3; + if (inp_len > 0 && inp[inp_len - 1] == '=') { + out_len--; + if (inp[inp_len - 2] == '=') { + out_len--; + } + } + if (out_len > size) { + throw new IllegalArgumentException( + "BASE64 data array is longer then destination buffer."); + } + int inp_pos = 0; + while (inp_pos < inp_len) { + int n0, n1, n2, n3; + char ch0 = inp[inp_pos++]; + char ch1 = inp[inp_pos++]; + char ch2 = inp[inp_pos++]; + char ch3 = inp[inp_pos++]; + if (ch0 >= char2int.length || (n0 = char2int[ch0]) < 0) { + throw new IllegalArgumentException("Illegal character " + ch0); + } + if (ch1 >= char2int.length || (n1 = char2int[ch1]) < 0) { + throw new IllegalArgumentException("Illegal character " + ch1); + } + buf[out_pos++] = (byte)((n0 << 2) | (n1 >> 4)); + if (ch2 == '=') break; + if (ch2 >= char2int.length || (n2 = char2int[ch2]) < 0) { + throw new IllegalArgumentException("Illegal character " + ch2); + } + buf[out_pos++] = (byte)((n1 << 4) | (n2 >> 2)); + if (ch3 == '=') break; + if (ch3 >= char2int.length || (n3 = char2int[ch3]) < 0) { + throw new IllegalArgumentException("Illegal character " + ch3); + } + buf[out_pos++] = (byte)((n2 << 6) | n3); + } + assert out_pos == offs + out_len; + } + while (out_pos < offs + size) buf[out_pos++] = 0; + } + + public static byte[] toByteArray(char[] inp) { + int inp_len = inp.length; + int out_len = inp_len / 4 * 3; + if (inp_len > 0 && inp[inp_len - 1] == '=') { + out_len--; + if (inp[inp_len - 2] == '=') { + out_len--; + } + } + byte[] buf = new byte[out_len]; + toByteArray(buf, 0, buf.length, inp); + return buf; + } + + /* + * See RFC 2045. + */ + private static final char int2char[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /* + * See RFC 2045 + */ + private static final byte char2int[] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51 + }; +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/ChannelTCP.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/ChannelTCP.java new file mode 100644 index 000000000..c50a5ad03 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/ChannelTCP.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.core; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketException; + +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.Protocol; + +public class ChannelTCP extends StreamChannel { + + private Socket socket; + private InputStream inp; + private OutputStream out; + private boolean closed; + + public ChannelTCP(IPeer peer, final String host, final int port) { + super(peer); + Thread thread = new Thread() { + public void run() { + try { + socket = new Socket(host, port); + socket.setTcpNoDelay(true); + inp = new BufferedInputStream(socket.getInputStream()); + out = new BufferedOutputStream(socket.getOutputStream()); + /* Uncomment for testing of buffers. + inp = new BufferedInputStream(new FilterInputStream(socket.getInputStream()) { + public int read() throws IOException { + System.out.println("Inp 1"); + return in.read(); + } + public int read(byte b[]) throws IOException { + int n = in.read(b); + System.out.println("Inp " + n); + return n; + } + public int read(byte b[], int off, int len) throws IOException { + int n = in.read(b, off, len); + System.out.println("Inp " + n); + return n; + } + }); + out = new BufferedOutputStream(new FilterOutputStream(socket.getOutputStream()){ + public void write(int b) throws IOException { + System.out.println("Out 1"); + out.write(b); + } + public void write(byte b[]) throws IOException { + System.out.println("Out " + b.length); + out.write(b); + } + public void write(byte b[], int off, int len) throws IOException { + System.out.println("Out " + len); + out.write(b, off, len); + } + }); + */ + Protocol.invokeLater(new Runnable() { + public void run() { + ChannelTCP.this.start(); + } + }); + } + catch (final IOException x) { + Protocol.invokeLater(new Runnable() { + public void run() { + ChannelTCP.this.terminate(x); + } + }); + } + } + }; + thread.setName("TCF Socket Connect"); + thread.start(); + } + + @Override + protected final int get() throws IOException { + try { + if (closed) return -1; + return inp.read(); + } + catch (SocketException x) { + if (closed) return -1; + throw x; + } + } + + @Override + protected final void put(int b) throws IOException { + assert b >= 0 && b <= 0xff; + if (closed) return; + out.write(b); + } + + @Override + protected final void flush() throws IOException { + if (closed) return; + out.flush(); + } + + @Override + protected void stop() throws IOException { + closed = true; + socket.close(); + out.close(); + inp.close(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Command.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Command.java new file mode 100644 index 000000000..d704ba96b --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/Command.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.core; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.internal.core.Token; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; + +/** + * This is utility class that helps to implement sending a command and receiving + * command result over TCF communication channel. The class uses JSON to encode + * command arguments and to decode result data. Clients are expected to subclass + * <code>Command</code> and override <code>done</code> method. + * + * Note: most clients don't need to handle protocol commands directly and + * can use service APIs instead. Service API does all command encoding/decoding + * for a client. + * + * Typical usage example: + * + * public IToken getContext(String id, final DoneGetContext done) { + * return new Command(channel, IService.this, "getContext", new Object[]{ id }) { + * @Override + * public void done(Exception error, Object[] args) { + * Context ctx = null; + * if (error == null) { + * assert args.length == 3; + * error = JSON.toError(args[0], args[1]); + * if (args[2] != null) ctx = new Context(args[2]); + * } + * done.doneGetContext(token, error, ctx); + * } + * }.token; + * } + */ +public abstract class Command implements IChannel.ICommandListener { + + private final IService service; + private final String command; + private final Object[] args; + + public final IToken token; + + private boolean done; + + public Command(IChannel channel, IService service, String command, Object[] args) { + this.service = service; + this.command = command; + this.args = args; + IToken t = null; + try { + t = channel.sendCommand(service, command, JSON.toJSONSequence(args), this); + } + catch (Throwable y) { + t = new Token(); + final Exception x = y instanceof Exception ? (Exception)y : new Exception(y); + Protocol.invokeLater(new Runnable() { + public void run() { + assert !done; + done = true; + done(x, null); + } + }); + } + token = t; + } + + public void progress(IToken token, byte[] data) { + assert this.token == token; + } + + public void result(IToken token, byte[] data) { + assert this.token == token; + Exception error = null; + Object[] args = null; + try { + args = JSON.parseSequence(data); + } + catch (Exception e) { + error = e; + } + assert !done; + done = true; + done(error, args); + } + + public void terminated(IToken token, Exception error) { + assert this.token == token; + assert !done; + done = true; + done(error, null); + } + + public abstract void done(Exception error, Object[] args); + + public String getCommandString() { + StringBuffer buf = new StringBuffer(); + buf.append(service.getName()); + buf.append(' '); + buf.append(command); + if (args != null) { + for (int i = 0; i < args.length; i++) { + buf.append(i == 0 ? " " : ", "); + try { + buf.append(JSON.toJSON(args[i])); + } + catch (IOException x) { + buf.append("***"); + buf.append(x.getMessage()); + buf.append("***"); + } + } + } + return buf.toString(); + } + + @SuppressWarnings("unchecked") + public static String toErrorString(Object data) { + if (data instanceof String) { + return (String)data; + } + else if (data != null) { + Map<String,Object> map = (Map<String,Object>)data; + Collection<Object> c = (Collection<Object>)map.get("params"); + return new MessageFormat((String)map.get("format")).format(c.toArray()); + } + return null; + } + + public Exception toError(Object code, Object data) { + int error_code = ((Number)code).intValue(); + if (error_code == 0) return null; + String cmd = getCommandString(); + if (cmd.length() > 72) cmd = cmd.substring(0, 72) + "..."; + return new Exception( + "TCF command exception:" + + "\nCommand: " + cmd + + "\nException: " + toErrorString(data) + + "\nError code: " + code); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/StreamChannel.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/StreamChannel.java new file mode 100644 index 000000000..a6b871740 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/core/StreamChannel.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.core; + +import java.io.IOException; + +import com.windriver.tcf.api.protocol.IPeer; + +public abstract class StreamChannel extends AbstractChannel { + + public static final int ESC = 3; + + public StreamChannel(IPeer peer) { + super(peer); + } + + protected abstract int get() throws IOException; + protected abstract void put(int n) throws IOException; + + @Override + protected final int read() throws IOException { + int res = get(); + if (res < 0) return EOS; + assert res >= 0 && res <= 0xff; + if (res != ESC) return res; + int n = get(); + switch (n) { + case 0: return ESC; + case 1: return EOM; + case 2: return EOS; + default: + if (n < 0) return EOS; + assert false; + return 0; + } + } + + @Override + protected final void write(int n) throws IOException { + switch (n) { + case ESC: put(ESC); put(0); break; + case EOM: put(ESC); put(1); break; + case EOS: put(ESC); put(2); break; + default: + assert n >= 0 && n <= 0xff; + put(n); + } + } + + @Override + protected void write(byte[] buf) throws IOException { + for (int i = 0; i < buf.length; i++) { + int n = buf[i] & 0xff; + put(n); + if (n == ESC) put(0); + } + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ChannelLoop.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ChannelLoop.java new file mode 100644 index 000000000..1bb1cd141 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ChannelLoop.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.core; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import com.windriver.tcf.api.core.StreamChannel; +import com.windriver.tcf.api.protocol.IPeer; + +public class ChannelLoop extends StreamChannel { + + private final byte[] buf = new byte[0x1000]; + private int buf_inp; + private int buf_out; + private boolean waiting; + private boolean closed; + + ChannelLoop(IPeer peer) { + super(peer); + start(); + } + + @Override + protected synchronized int get() throws IOException { + try { + while (buf_inp == buf_out) { + if (closed) return -1; + waiting = true; + wait(); + } + int b = buf[buf_out] & 0xff; + buf_out = (buf_out + 1) % buf.length; + if (waiting) { + waiting = false; + notifyAll(); + } + return b; + } + catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + + @Override + protected synchronized void put(int b) throws IOException { + assert b >=0 && b <= 0xff; + try { + for (;;) { + int nxt_inp = (buf_inp + 1) % buf.length; + if (nxt_inp != buf_out) { + buf[buf_inp] = (byte)b; + buf_inp = nxt_inp; + break; + } + if (closed) return; + waiting = true; + wait(); + } + if (waiting) { + waiting = false; + notifyAll(); + } + } + catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + + @Override + protected void flush() throws IOException { + } + + @Override + protected synchronized void stop() throws IOException { + closed = true; + if (waiting) { + waiting = false; + notifyAll(); + } + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/LocalPeer.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/LocalPeer.java new file mode 100644 index 000000000..9300e3ad4 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/LocalPeer.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.core; + +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.AbstractPeer; +import com.windriver.tcf.api.protocol.IChannel; + +public class LocalPeer extends AbstractPeer { + + private static Map<String, String> createAttributes() { + Map<String, String> attrs = new HashMap<String, String>(); + attrs.put(ATTR_ID, "TCFLocal"); + attrs.put(ATTR_NAME, "Local Peer"); + attrs.put(ATTR_OS_NAME, System.getProperty("os.name")); + attrs.put(ATTR_TRANSPORT_NAME, "Loop"); + return attrs; + } + + public LocalPeer() { + super(createAttributes()); + } + + public IChannel openChannel() { + return new ChannelLoop(this); + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyCollection.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyCollection.java new file mode 100644 index 000000000..cc8aeb422 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyCollection.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.core; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +public class ReadOnlyCollection<E> implements Set<E> { + + private final Collection<E> base; + + public ReadOnlyCollection(Collection<E> base) { + this.base = base; + } + + private void error() { + throw new Error("Read only Collection"); + } + + public boolean add(E e) { + error(); + return false; + } + + public boolean addAll(Collection<? extends E> c) { + error(); + return false; + } + + public void clear() { + error(); + } + + public boolean contains(Object o) { + return base.contains(o); + } + + public boolean containsAll(Collection<?> c) { + return base.containsAll(c); + } + + public boolean isEmpty() { + return base.isEmpty(); + } + + public Iterator<E> iterator() { + final Iterator<E> iterator = base.iterator(); + return new Iterator<E>() { + + public boolean hasNext() { + return iterator.hasNext(); + } + + public E next() { + return iterator.next(); + } + + public void remove() { + error(); + } + }; + } + + public boolean remove(Object o) { + error(); + return false; + } + + public boolean removeAll(Collection<?> c) { + error(); + return false; + } + + public boolean retainAll(Collection<?> c) { + error(); + return false; + } + + public int size() { + return base.size(); + } + + public Object[] toArray() { + return base.toArray(); + } + + public <T> T[] toArray(T[] a) { + return base.toArray(a); + } + + public boolean equals(Object o) { + return base.equals(o); + } + + public int hashCode() { + return base.hashCode(); + } + + public String toString() { + return base.toString(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyMap.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyMap.java new file mode 100644 index 000000000..458d27f72 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/ReadOnlyMap.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.core; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class ReadOnlyMap<K,V> implements Map<K,V> { + + private final Map<K,V> base; + private Set<K> key_set; + private Set<Map.Entry<K, V>> entry_set; + private Collection<V> values; + + public ReadOnlyMap(Map<K,V> base) { + this.base = base; + } + + private void error() { + throw new Error("Read only Map"); + } + + public void clear() { + error(); + } + + public boolean containsKey(Object key) { + return base.containsKey(key); + } + + public boolean containsValue(Object value) { + return base.containsValue(value); + } + + public Set<Map.Entry<K, V>> entrySet() { + if (entry_set == null) entry_set = new ReadOnlyCollection<Map.Entry<K, V>>(base.entrySet()); + return entry_set; + } + + public V get(Object key) { + return base.get(key); + } + + public boolean isEmpty() { + return base.isEmpty(); + } + + public Set<K> keySet() { + if (key_set == null) key_set = new ReadOnlyCollection<K>(base.keySet()); + return key_set; + } + + public V put(K key, V value) { + error(); + return null; + } + + public void putAll(Map<? extends K, ? extends V> m) { + error(); + } + + public V remove(Object key) { + error(); + return null; + } + + public int size() { + return base.size(); + } + + public Collection<V> values() { + if (values == null) values = new ReadOnlyCollection<V>(base.values()); + return values; + } + + public boolean equals(Object o) { + return base.equals(o); + } + + public int hashCode() { + return base.hashCode(); + } + + public String toString() { + return base.toString(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/RemotePeer.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/RemotePeer.java new file mode 100644 index 000000000..fa4c93eef --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/RemotePeer.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.core; + +import java.util.Iterator; +import java.util.Map; + +import com.windriver.tcf.api.core.AbstractPeer; +import com.windriver.tcf.api.core.ChannelTCP; +import com.windriver.tcf.api.protocol.IChannel; + +public class RemotePeer extends AbstractPeer { + + public RemotePeer(Map<String,String> attrs) { + super(attrs); + } + + public boolean updateAttributes(Map<String,String> attrs1) { + boolean equ = true; + Map<String,String> attrs0 = getAttributesStorage(); + assert attrs1.get(ATTR_ID).equals(attrs0.get(ATTR_ID)); + for (Iterator<String> i = attrs0.keySet().iterator(); i.hasNext();) { + String key = i.next(); + if (!attrs0.get(key).equals(attrs1.get(key))) { + equ = false; + break; + } + } + for (Iterator<String> i = attrs1.keySet().iterator(); i.hasNext();) { + String key = i.next(); + if (!attrs1.get(key).equals(attrs0.get(key))) { + equ = false; + break; + } + } + if (!equ) { + attrs0.clear(); + attrs0.putAll(attrs1); + } + return !equ; + } + + public IChannel openChannel() { + String transport = getTransportName(); + if (transport.equals("TCP")) { + Map<String,String> attrs = getAttributes(); + String host = attrs.get(ATTR_IP_HOST); + String port = attrs.get(ATTR_IP_PORT); + if (host == null) throw new Error("No host name"); + if (port == null) throw new Error("No port number"); + return new ChannelTCP(this, host, Integer.parseInt(port)); + } + else { + throw new Error("Unknow transport name: " + transport); + } + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Token.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Token.java new file mode 100644 index 000000000..9892b9cbd --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Token.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.core; + +import java.io.UnsupportedEncodingException; + +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; + +public class Token implements IToken { + + private static int cnt = 0; + + private final String id; + private final byte[] bytes; + private final IChannel.ICommandListener listener; + + public Token() { + id = null; + bytes = null; + listener = null; + } + + public Token(IChannel.ICommandListener listener) { + this.listener = listener; + id = Integer.toString(cnt++); + try { + bytes = id.getBytes("ASCII"); + } + catch (UnsupportedEncodingException e) { + throw new Error(e); + } + } + + public Token(byte[] bytes) { + this.bytes = bytes; + listener = null; + try { + id = new String(bytes, "ASCII"); + } + catch (UnsupportedEncodingException e) { + throw new Error(e); + } + } + + public boolean cancel() { + return false; + } + + public String getID() { + return id; + } + + public byte[] getBytes() { + return bytes; + } + + public IChannel.ICommandListener getListener() { + return listener; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Transport.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Transport.java new file mode 100644 index 000000000..3e2d1840e --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/core/Transport.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.core; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; + +import com.windriver.tcf.api.Activator; +import com.windriver.tcf.api.core.AbstractChannel; +import com.windriver.tcf.api.core.AbstractPeer; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.ILocator; + +public class Transport { + + private static final Collection<AbstractChannel> channels = + new LinkedList<AbstractChannel>(); + private static final Collection<Protocol.ChannelOpenListener> listeners = + new LinkedList<Protocol.ChannelOpenListener>(); + + public static void channelOpened(final AbstractChannel channel) { + channels.add(channel); + for (Protocol.ChannelOpenListener l : listeners) { + try { + l.onChannelOpen(channel); + } + catch (Throwable x) { + Activator.log("Exception in channel listener", x); + } + } + } + + public static void channelClosed(final AbstractChannel channel, final Throwable x) { + channels.remove(channel); + } + + public static IChannel[] getOpenChannels() { + return channels.toArray(new IChannel[channels.size()]); + } + + public static void addChanalOpenListener(Protocol.ChannelOpenListener listener) { + listeners.add(listener); + } + + public static void removeChanalOpenListener(Protocol.ChannelOpenListener listener) { + listeners.remove(listener); + } + + public static void peerDisposed(AbstractPeer peer) { + Collection<AbstractChannel> bf = new ArrayList<AbstractChannel>(channels); + for (Iterator<AbstractChannel> i = bf.iterator(); i.hasNext();) { + AbstractChannel c = i.next(); + if (c.getRemotePeer() != peer) continue; + c.close(); + } + } + + /** + * Transmit TCF event message. + * The message is sent to all open communication channels – broadcasted. + * + * This is internal API, TCF clients should use {@code com.windriver.tcf.api.protocol.Protocol}. + */ + public static void sendEvent(String service_name, String event_name, byte[] data) { + for (Iterator<AbstractChannel> i = channels.iterator(); i.hasNext();) { + AbstractChannel channel = i.next(); + IService s = channel.getRemoteService(service_name); + if (s != null) channel.sendEvent(s, event_name, data); + } + } + + /** + * Call back after TCF messages sent by this host up to this moment are delivered + * to their intended targets. This method is intended for synchronization of messages + * across multiple channels. + * + * Note: Cross channel synchronization can reduce performance and throughput. + * Most clients don't need cross channel synchronization and should not call this method. + * + * @param done will be executed by dispatch thread after communication + * messages are delivered to corresponding targets. + * + * This is internal API, TCF clients should use {@code com.windriver.tcf.api.protocol.Protocol}. + */ + public static void sync(final Runnable done) { + final Set<IToken> set = new HashSet<IToken>(); + ILocator.DoneSync done_sync = new ILocator.DoneSync() { + public void doneSync(IToken token) { + assert set.contains(token); + set.remove(token); + if (set.isEmpty()) done.run(); + } + }; + for (Iterator<AbstractChannel> i = channels.iterator(); i.hasNext();) { + AbstractChannel channel = i.next(); + ILocator s = channel.getRemoteService(ILocator.class); + if (s != null) set.add(s.sync(done_sync)); + } + if (set.isEmpty()) Protocol.invokeLater(done); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/DiagnosticsService.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/DiagnosticsService.java new file mode 100644 index 000000000..88ad937bb --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/DiagnosticsService.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.local; + +import com.windriver.tcf.api.internal.core.Token; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IDiagnostics; + +public class DiagnosticsService implements IDiagnostics { + + private final IChannel channel; + + private class CommandServer implements IChannel.ICommandServer { + + public void command(IToken token, String name, byte[] data) { + try { + if (name.equals("echo")) { + channel.sendResult(token, data); + } + else if (name.equals("getTestList")) { + channel.sendResult(token, JSON.toJSONSequence(new Object[]{ + new Integer(0), null, new String[0]})); + } + else { + channel.terminate(new Exception("Illegal command: " + name)); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + } + + public DiagnosticsService(IChannel channel) { + this.channel = channel; + channel.addCommandServer(this, new CommandServer()); + } + + public String getName() { + return NAME; + } + + public IToken echo(final String s, final DoneEcho done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneEcho(token, null, s); + } + }); + return token; + } + + public IToken getTestList(final DoneGetTestList done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneGetTestList(token, null, new String[0]); + } + }); + return token; + } + + public IToken runTest(final String s, final DoneRunTest done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneRunTest(token, new Exception("Test suite not found: " + s), null); + } + }); + return token; + } + + public IToken cancelTest(String context_id, final DoneCancelTest done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneCancelTest(token, null); + } + }); + return token; + } + + public IToken getSymbol(String context_id, String symbol_name, final DoneGetSymbol done) { + final IToken token = new Token(); + Protocol.invokeLater(new Runnable() { + public void run() { + done.doneGetSymbol(token, new Exception("Invalid context"), null); + } + }); + return token; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/LocatorService.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/LocatorService.java new file mode 100644 index 000000000..9672433ea --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/local/LocatorService.java @@ -0,0 +1,368 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.local; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.windriver.tcf.api.Activator; +import com.windriver.tcf.api.core.AbstractChannel; +import com.windriver.tcf.api.internal.core.LocalPeer; +import com.windriver.tcf.api.internal.core.RemotePeer; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ILocator; + +/** + * Locator service uses transport layer to search + * for peers and to collect and maintain up-to-date + * data about peer’s attributes and capabilities (services). + */ +public class LocatorService implements ILocator { + + private static LocatorService locator; + private static final Map<String,IPeer> peers = new HashMap<String,IPeer>(); + private static final Collection<LocatorListener> listeners = new ArrayList<LocatorListener>(); + + private static LocalPeer local_peer; + + private DatagramSocket socket; + + private Thread output_thread = new Thread() { + public void run() { + for (;;) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + sendPeerInfoRequest(); + } + }); + try { + sleep(5 * 1000); + } + catch (InterruptedException x) { + break; + } + } + } + }; + + private Thread input_thread = new Thread() { + public void run() { + for (;;) { + try { + byte[] buf = new byte[0x1000]; + final DatagramPacket p = new DatagramPacket(buf, buf.length); + socket.receive(p); + Protocol.invokeAndWait(new Runnable() { + public void run() { + handleDatagramPacket(p); + } + }); + } + catch (Exception x) { + Activator.log("Cannot read from datagram socket", x); + break; + } + } + } + }; + + public LocatorService() { + locator = this; + try { + socket = new DatagramSocket(); + socket.setBroadcast(true); + input_thread.setName("TCF Locator Receiver"); + output_thread.setName("TCF Locator Transmitter"); + input_thread.start(); + output_thread.start(); + } + catch (Exception x) { + Activator.log("Cannot create datagram socket", x); + } + } + + public static LocalPeer getLocalPeer() { + return local_peer; + } + + public static void addPeer(IPeer peer) { + assert peers.get(peer.getID()) == null; + if (peer instanceof LocalPeer) local_peer = (LocalPeer)peer; + peers.put(peer.getID(), peer); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext(); ) { + i.next().peerAdded(peer); + } + } + + public static void removePeer(IPeer peer) { + assert peers.get(peer.getID()) == peer; + peers.remove(peer); + String id = peer.getID(); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext(); ) { + i.next().peerRemoved(id); + } + } + + private void notifyPeer(IPeer peer) { + assert peers.get(peer.getID()) == peer; + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext(); ) { + i.next().peerChanged(peer); + } + } + + public static void channelStarted(final AbstractChannel channel) { + channel.addEventListener(locator, new IChannel.IEventListener() { + public void event(String name, byte[] data) { + locator.event(channel, name, data); + } + }); + channel.addCommandServer(locator, new IChannel.ICommandServer() { + public void command(IToken token, String name, byte[] data) { + locator.command(channel, token, name, data); + } + }); + } + + @SuppressWarnings("unchecked") + private void event(AbstractChannel channel, String name, byte[] data) { + try { + if (name.equals("Hello")) { + Collection<String> c = (Collection<String>)JSON.parseSequence(data)[0]; + channel.onLocatorHello(c); + } + else { + throw new IOException("Unknown event: Locator." + name); + } + } + catch (IOException e) { + channel.terminate(e); + } + } + + private void command(AbstractChannel channel, IToken token, String name, byte[] data) { + try { + if (name.equals("redirect")) { + // String peer_id = (String)JSON.parseSequence(data)[0]; + // TODO: perform local ILocator.redirect + channel.sendResult(token, JSON.toJSONSequence(new Object[]{ + new Integer(0), null })); + } + else if (name.equals("sync")) { + channel.sendResult(token, null); + } + else { + channel.terminate(new Exception("Illegal command: " + name)); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + + private void sendPeerInfoRequest() { + try { + byte[] buf = new byte[8]; + int i = 0; + buf[i++] = 'T'; + buf[i++] = 'C'; + buf[i++] = 'F'; + buf[i++] = '1'; + buf[i++] = CONF_REQ_INFO; + buf[i++] = 0; + buf[i++] = 0; + buf[i++] = 0; + for (Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements();) { + NetworkInterface f = e.nextElement(); + /* TODO: Class InterfaceAddress does not exists in Java versions before 1.6. + * When support for old Java versions is not needed any more, + * the code below should be replaced with: + * for (InterfaceAddress ia : f.getInterfaceAddresses()) { + * socket.send(new DatagramPacket(buf, buf.length, ia.getBroadcast(), 1534)); + * } + */ + Enumeration<InetAddress> n = f.getInetAddresses(); + while (n.hasMoreElements()) { + InetAddress ina = n.nextElement(); + byte[] adr = ina.getAddress(); + if (adr.length != 4) { + // TODO: Support IPv6 + // System.out.println("Dont support IPv6: " + ina); + continue; + } + /* Since we don't know actual broadcast address, + * lets try different combinations. + * Hopefully one of them will work. + */ + int h = adr[0] & 0xff; + if (h >= 1 && h <= 127 && h != 38) { + adr[3] = (byte)255; + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[2] = (byte)255; + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[1] = (byte)255; + } + else if (h >= 128 && h <= 191) { + adr[3] = (byte)255; + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[2] = (byte)255; + } + else { + adr[3] = (byte)(adr[3] | 0x0f); + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[3] = (byte)(adr[3] | 0x01f); + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[3] = (byte)(adr[3] | 0x03f); + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[3] = (byte)(adr[3] | 0x07f); + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + adr[3] = (byte)255; + } + socket.send(new DatagramPacket(buf, buf.length, + InetAddress.getByAddress(null, adr), 1534)); + } + } + } + catch (Exception x) { + Activator.log("Cannot send datagram packet", x); + } + } + + private void handleDatagramPacket(DatagramPacket p) { + try { + byte[] buf = p.getData(); + int len = p.getLength(); + if (len < 8) return; + if (buf[0] != 'T') return; + if (buf[1] != 'C') return; + if (buf[2] != 'F') return; + if (buf[3] != '1') return; + switch (buf[4]) { + case CONF_PEER_INFO: + handlePeerInfoPacket(p); + break; + case CONF_REQ_INFO: + handleReqInfoPacket(p); + break; + } + } + catch (Throwable x) { + Activator.log("Invalid datagram packet received", x); + } + } + + private void handlePeerInfoPacket(DatagramPacket p) throws Exception { + Map<String,String> map = new HashMap<String,String>(); + String s = new String(p.getData(), 8, p.getLength() - 8, "UTF8"); + int len = s.length(); + int i = 0; + while (i < len) { + int i0 = i; + while (i < len && s.charAt(i) != '=' && s.charAt(i) != 0) i++; + int i1 = i; + if (i < len && s.charAt(i) == '=') i++; + int i2 = i; + while (i < len && s.charAt(i) != 0) i++; + int i3 = i; + if (i < len && s.charAt(i) == 0) i++; + String key = s.substring(i0, i1); + String val = s.substring(i2, i3); + map.put(key, val); + } + String id = map.get(IPeer.ATTR_ID); + if (id == null) throw new Exception("Invalid peer info: no ID"); + IPeer peer = peers.get(id); + if (peer instanceof RemotePeer) { + if (((RemotePeer)peer).updateAttributes(map)) { + notifyPeer(peer); + } + } + else { + new RemotePeer(map); + } + } + + private void handleReqInfoPacket(DatagramPacket p) { + byte[] buf = p.getData(); + int len = p.getLength(); + // TODO: handleReqInfoPacket() + } + + /*----------------------------------------------------------------------------------*/ + + /* + * Return local instance of Locator service + */ + public static LocatorService getLocator() { + return locator; + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#getName() + */ + public String getName() { + return NAME; + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#getPeers() + */ + public Map<String,IPeer> getPeers() { + return peers; + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#redirect() + */ + public IToken redirect(String peer_id, DoneRedirect done) { + throw new Error("Channel redirect cannot be done on local peer"); + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#sync() + */ + public IToken sync(DoneSync done) { + throw new Error("Channel sync cannot be done on local peer"); + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#addListener(com.windriver.tcf.api.protocol.ILocator.Listener) + */ + public void addListener(LocatorListener listener) { + listeners.add(listener); + } + + /* (non-Javadoc) + * @see com.windriver.tcf.api.protocol.ILocator#removeListener(com.windriver.tcf.api.protocol.ILocator.Listener) + */ + public void removeListener(LocatorListener listener) { + listeners.remove(listener); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/BreakpointsProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/BreakpointsProxy.java new file mode 100644 index 000000000..475ff79fe --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/BreakpointsProxy.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.services.IBreakpoints; + +public class BreakpointsProxy implements IBreakpoints { + + private final IChannel channel; + private final Map<BreakpointsListener,IChannel.IEventListener> listeners = + new HashMap<BreakpointsListener,IChannel.IEventListener>(); + + public BreakpointsProxy(IChannel channel) { + this.channel = channel; + } + + public IToken set(Map<String, Object>[] properties, final DoneCommand done) { + return new Command(channel, this, "set", new Object[]{ properties }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken add(Map<String, Object> properties, final DoneCommand done) { + return new Command(channel, this, "add", new Object[]{ properties }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken change(Map<String, Object> properties, final DoneCommand done) { + return new Command(channel, this, "change", new Object[]{ properties }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken disable(String[] ids, final DoneCommand done) { + return new Command(channel, this, "disable", new Object[]{ ids }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken enable(String[] ids, final DoneCommand done) { + return new Command(channel, this, "enable", new Object[]{ ids }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken remove(String[] ids, final DoneCommand done) { + return new Command(channel, this, "remove", new Object[]{ ids }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken getIDs(final DoneGetIDs done) { + return new Command(channel, this, "getIDs", null) { + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetIDs(token, error, arr); + } + }.token; + } + + public IToken getProperties(String id, final DoneGetProperties done) { + return new Command(channel, this, "getProperties", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + Map<String,Object> map = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + map = (Map<String,Object>)args[2]; + } + done.doneGetProperties(token, error, map); + } + }.token; + } + + public IToken getStatus(String id, final DoneGetStatus done) { + return new Command(channel, this, "getStatus", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + Map<String,Object> map = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + map = (Map<String,Object>)args[2]; + } + done.doneGetStatus(token, error, map); + } + }.token; + } + + public String getName() { + return NAME; + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } + + public void addListener(final BreakpointsListener listener) { + IChannel.IEventListener l = new IChannel.IEventListener() { + + @SuppressWarnings("unchecked") + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("status")) { + assert args.length == 2; + listener.breakpointStatusChanged((String)args[0], (Map<String,Object>)args[1]); + } + else { + throw new IOException("Breakpoints service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + channel.addEventListener(this, l); + listeners.put(listener, l); + } + + public void removeListener(BreakpointsListener listener) { + IChannel.IEventListener l = listeners.get(listener); + if (l != null) channel.removeEventListener(this, l); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/DiagnosticsProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/DiagnosticsProxy.java new file mode 100644 index 000000000..cafcb845e --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/DiagnosticsProxy.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IDiagnostics; + +public class DiagnosticsProxy implements IDiagnostics { + + private final IChannel channel; + + private static class Symbol implements ISymbol { + + private final Map<String,Object> props; + + Symbol(Map<String,Object> props) { + this.props = props; + } + + public String getSectionName() { + return (String)props.get("Section"); + } + + public Number getValue() { + return (Number)props.get("Value"); + } + + public boolean isAbs() { + Boolean b = (Boolean)props.get("Abs"); + return b != null && b.booleanValue(); + } + + public boolean isCommon() { + String s = (String)props.get("Storage"); + return s != null && s.equals("COMMON"); + } + + public boolean isGlobal() { + String s = (String)props.get("Storage"); + return s != null && s.equals("GLOBAL"); + } + + public boolean isLocal() { + String s = (String)props.get("Storage"); + return s != null && s.equals("LOCAL"); + } + + public boolean isUndef() { + String s = (String)props.get("Storage"); + return s != null && s.equals("UNDEF"); + } + } + + public DiagnosticsProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken echo(String s, final DoneEcho done) { + return new Command(channel, this, "echo", new Object[]{ s }) { + @Override + public void done(Exception error, Object[] args) { + String str = null; + if (error == null) { + assert args.length == 1; + str = (String)args[0]; + } + done.doneEcho(token, error, str); + } + }.token; + } + + public IToken getTestList(final DoneGetTestList done) { + return new Command(channel, this, "getTestList", null) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray((Collection<String>)args[2]); + } + done.doneGetTestList(token, error, arr); + } + }.token; + } + + public IToken runTest(String s, final DoneRunTest done) { + return new Command(channel, this, "runTest", new Object[]{ s }) { + @Override + public void done(Exception error, Object[] args) { + String str = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + str = (String)args[2]; + } + done.doneRunTest(token, error, str); + } + }.token; + } + + public IToken cancelTest(String s, final DoneCancelTest done) { + return new Command(channel, this, "cancelTest", new Object[]{ s }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCancelTest(token, error); + } + }.token; + } + + public IToken getSymbol(String context_id, String symbol_name, final DoneGetSymbol done) { + return new Command(channel, this, "getSymbol", new Object[]{ context_id, symbol_name }) { + @Override + public void done(Exception error, Object[] args) { + ISymbol sym = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + sym = toSymbol(args[2]); + } + done.doneGetSymbol(token, error, sym); + } + }.token; + } + + private String[] toStringArray(Collection<String> c) { + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } + + @SuppressWarnings("unchecked") + private ISymbol toSymbol(Object o) { + if (o == null) return null; + return new Symbol((Map<String,Object>)o); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/FileSystemProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/FileSystemProxy.java new file mode 100644 index 000000000..38d8678ef --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/FileSystemProxy.java @@ -0,0 +1,582 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Base64; +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IFileSystem; + +public class FileSystemProxy implements IFileSystem { + + private final class FileHandle implements IFileHandle { + final String id; + + FileHandle(String id) { + this.id = id; + } + + public IFileSystem getService() { + return FileSystemProxy.this; + } + + public String toString() { + return "[File Handle '" + id + "']"; + } + } + + private static final class Status extends FileSystemException { + + private static final long serialVersionUID = -1636567076145085980L; + + private final int status; + + Status(int status, String message) { + super(message); + this.status = status; + } + + Status(Exception x) { + super(x); + this.status = STATUS_FAILURE; + } + + public int getStatus() { + return status; + } + } + + private abstract class FileSystemCommand extends Command { + + FileSystemCommand(String command, Object[] args) { + super(channel, FileSystemProxy.this, command, args); + } + + public Status toFSError(Object code, Object data) { + int error_code = ((Number)code).intValue(); + if (error_code == 0) return null; + String cmd = getCommandString(); + if (cmd.length() > 32) cmd = cmd.substring(0, 32) + "..."; + return new Status(error_code, + "TCF command exception:" + + "\nCommand: " + cmd + + "\nException: " + toErrorString(data) + + "\nError code: " + code); + } + } + + private final IChannel channel; + + public FileSystemProxy(IChannel channel) { + this.channel = channel; + } + + public IToken close(IFileHandle handle, final DoneClose done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("close", new Object[]{ id }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneClose(token, s); + } + }.token; + } + + public IToken setstat(String path, FileAttrs attrs, final DoneSetStat done) { + Object dt = toObject(attrs); + return new FileSystemCommand("setstat", new Object[]{ path, dt }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneSetStat(token, s); + } + }.token; + } + + public IToken fsetstat(IFileHandle handle, FileAttrs attrs, final DoneSetStat done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + Object dt = toObject(attrs); + return new FileSystemCommand("fsetstat", new Object[]{ id, dt }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneSetStat(token, s); + } + }.token; + } + + public IToken stat(String path, final DoneStat done) { + return new FileSystemCommand("stat", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileAttrs a = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + a = toFileAttrs(args[2]); + } + } + done.doneStat(token, s, a); + } + }.token; + } + + public IToken fstat(IFileHandle handle, final DoneStat done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("fstat", new Object[]{ id }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileAttrs a = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + a = toFileAttrs(args[2]); + } + } + done.doneStat(token, s, a); + } + }.token; + } + + public IToken lstat(String path, final DoneStat done) { + return new FileSystemCommand("lstat", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileAttrs a = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + a = toFileAttrs(args[2]); + } + } + done.doneStat(token, s, a); + } + }.token; + } + + public IToken mkdir(String path, FileAttrs attrs, final DoneMkDir done) { + Object dt = toObject(attrs); + return new FileSystemCommand("mkdir", new Object[]{ path, dt }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneMkDir(token, s); + } + }.token; + } + + public IToken open(String file_name, int flags, FileAttrs attrs, final DoneOpen done) { + Object dt = toObject(attrs); + return new FileSystemCommand("open", new Object[]{ file_name, new Integer(flags), dt }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileHandle h = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + h = toFileHandle(args[2]); + } + } + done.doneOpen(token, s, h); + } + }.token; + } + + public IToken opendir(String path, final DoneOpen done) { + return new FileSystemCommand("opendir", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + FileHandle h = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + h = toFileHandle(args[2]); + } + } + done.doneOpen(token, s, h); + } + }.token; + } + + public IToken read(IFileHandle handle, long offset, int len, final DoneRead done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("read", new Object[]{ + id, Long.valueOf(offset), Integer.valueOf(len) }) { + public void done(Exception error, Object[] args) { + Status s = null; + byte[] b = null; + boolean eof = false; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 4; + s = toFSError(args[1], args[2]); + if (s == null) { + String str = (String)args[0]; + if (str != null) b = Base64.toByteArray(str.toCharArray()); + eof = ((Boolean)args[3]).booleanValue(); + } + } + done.doneRead(token, s, b, eof); + } + }.token; + } + + public IToken readdir(IFileHandle handle, final DoneReadDir done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("readdir", new Object[]{ id }) { + public void done(Exception error, Object[] args) { + Status s = null; + DirEntry[] b = null; + boolean eof = false; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 4; + s = toFSError(args[1], args[2]); + if (s == null) { + b = toDirEntryArray(args[0]); + eof = ((Boolean)args[3]).booleanValue(); + } + } + done.doneReadDir(token, s, b, eof); + } + }.token; + } + + public IToken roots(final DoneRoots done) { + return new FileSystemCommand("roots", null) { + public void done(Exception error, Object[] args) { + Status s = null; + DirEntry[] b = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[1], args[2]); + if (s == null) { + b = toDirEntryArray(args[0]); + } + } + done.doneRoots(token, s, b); + } + }.token; + } + + public IToken readlink(String path, final DoneReadLink done) { + return new FileSystemCommand("readlink", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + String p = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + p = (String)args[2]; + } + } + done.doneReadLink(token, s, p); + } + }.token; + } + + public IToken realpath(String path, final DoneRealPath done) { + return new FileSystemCommand("realpath", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + String p = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 3; + s = toFSError(args[0], args[1]); + if (s == null) { + p = (String)args[2]; + } + } + done.doneRealPath(token, s, p); + } + }.token; + } + + public IToken remove(String file_name, final DoneRemove done) { + return new FileSystemCommand("remove", new Object[]{ file_name }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneRemove(token, s); + } + }.token; + } + + public IToken rename(String old_path, String new_path, final DoneRename done) { + return new FileSystemCommand("rename", new Object[]{ old_path, new_path }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneRename(token, s); + } + }.token; + } + + public IToken rmdir(String path, final DoneRemove done) { + return new FileSystemCommand("rmdir", new Object[]{ path }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneRemove(token, s); + } + }.token; + } + + public IToken symlink(String link_path, String target_path, final DoneSymLink done) { + return new FileSystemCommand("symlink", new Object[]{ link_path, target_path }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneSymLink(token, s); + } + }.token; + } + + public IToken write(IFileHandle handle, long offset, byte[] data, + int data_pos, int data_size, final DoneWrite done) { + assert handle.getService() == this; + String id = ((FileHandle)handle).id; + return new FileSystemCommand("write", new Object[]{ + id, Long.valueOf(offset), Base64.toBase64(data, data_pos, data_size) }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneWrite(token, s); + } + }.token; + } + + public IToken copy(String src_path, String dst_path, + boolean copy_permissions, boolean copy_uidgid, final DoneCopy done) { + return new FileSystemCommand("copy", new Object[]{ + src_path, dst_path, Boolean.valueOf(copy_permissions), + Boolean.valueOf(copy_uidgid) }) { + public void done(Exception error, Object[] args) { + Status s = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 2; + s = toFSError(args[0], args[1]); + } + done.doneCopy(token, s); + } + }.token; + } + + public IToken user(final DoneUser done) { + return new FileSystemCommand("user", null) { + @Override + public void done(Exception error, Object[] args) { + Status s = null; + int r_uid = 0; + int e_uid = 0; + int r_gid = 0; + int e_gid = 0; + String home = null; + if (error != null) { + s = new Status(error); + } + else { + assert args.length == 5; + r_uid = ((Number)args[0]).intValue(); + e_uid = ((Number)args[1]).intValue(); + r_gid = ((Number)args[2]).intValue(); + e_gid = ((Number)args[3]).intValue(); + home = (String)args[4]; + } + done.doneUser(token, s, r_uid, e_uid, r_gid, e_gid, home); + } + }.token; + } + + public String getName() { + return NAME; + } + + private Object toObject(FileAttrs attrs) { + if (attrs == null) return null; + Map<String,Object> m = new HashMap<String,Object>(); + if (attrs.attributes != null) m.putAll(attrs.attributes); + if ((attrs.flags & ATTR_SIZE) != 0) { + m.put("Size", Long.valueOf(attrs.size)); + } + if ((attrs.flags & ATTR_UIDGID) != 0) { + m.put("UID", Integer.valueOf(attrs.uid)); + m.put("GID", Integer.valueOf(attrs.gid)); + } + if ((attrs.flags & ATTR_PERMISSIONS) != 0) { + m.put("Permissions", Integer.valueOf(attrs.permissions)); + } + if ((attrs.flags & ATTR_ACMODTIME) != 0) { + m.put("ATime", Long.valueOf(attrs.atime)); + m.put("MTime", Long.valueOf(attrs.mtime)); + } + return m; + } + + @SuppressWarnings("unchecked") + private FileAttrs toFileAttrs(Object o) { + if (o == null) return null; + Map<String,Object> m = new HashMap<String,Object>((Map<String,Object>)o); + int flags = 0; + long size = 0; + int uid = 0; + int gid = 0; + int permissions = 0; + long atime = 0; + long mtime = 0; + Number n = (Number)m.remove("Size"); + if (n != null) { + size = n.longValue(); + flags |= ATTR_SIZE; + } + Number n1 = (Number)m.remove("UID"); + Number n2 = (Number)m.remove("GID"); + if (n1 != null && n2 != null) { + uid = n1.intValue(); + gid = n2.intValue(); + flags |= ATTR_UIDGID; + } + n = (Number)m.remove("Permissions"); + if (n != null) { + permissions = n.intValue(); + flags |= ATTR_PERMISSIONS; + } + n1 = (Number)m.remove("ATime"); + n2 = (Number)m.remove("MTime"); + if (n1 != null && n2 != null) { + atime = n1.longValue(); + mtime = n2.longValue(); + flags |= ATTR_ACMODTIME; + } + return new FileAttrs(flags, size, uid, gid, permissions, atime, mtime, m); + } + + private FileHandle toFileHandle(Object o) { + if (o == null) return null; + return new FileHandle(o.toString()); + } + + @SuppressWarnings("unchecked") + private DirEntry[] toDirEntryArray(Object o) { + if (o == null) return null; + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + DirEntry[] res = new DirEntry[c.size()]; + int i = 0; + for (Map<String,Object> m : c) { + res[i++] = new DirEntry( + (String)m.get("FileName"), + (String)m.get("LongName"), + toFileAttrs(m.get("Attrs"))); + } + return res; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/GenericProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/GenericProxy.java new file mode 100644 index 000000000..36293b5c2 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/GenericProxy.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IService; + +/** + * Objects of GenericProxy class represent remote services, which don't + * have a proxy class defined for them. + * Clients still can use such services, but framework will not provide + * service specific utility methods for message formatting and parsing. + */ +public class GenericProxy implements IService { + + private final IChannel channel; + private final String name; + + public GenericProxy(IChannel channel, String name) { + this.channel = channel; + this.name = name; + } + + public String getName() { + return name; + } + + public IChannel getChannel() { + return channel; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LineNumbersProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LineNumbersProxy.java new file mode 100644 index 000000000..51e32cd7f --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LineNumbersProxy.java @@ -0,0 +1,79 @@ +package com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.ILineNumbers; + +public class LineNumbersProxy implements ILineNumbers { + + private final IChannel channel; + + public LineNumbersProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken mapToSource(String context_id, Number start_address, + Number end_address, final DoneMapToSource done) { + return new Command(channel, this, "mapToSource", new Object[]{ context_id, + start_address, end_address }) { + @Override + public void done(Exception error, Object[] args) { + CodeArea[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toTextAreaArray(args[2]); + } + done.doneMapToSource(token, error, arr); + } + }.token; + } + + private static int getInteger(Map<String,Object> map, String name, int def) { + Number n = (Number)map.get(name); + if (n == null) return def; + return n.intValue(); + } + + private static String getString(Map<String,Object> map, String name, String def) { + String s = (String)map.get(name); + if (s == null) return def; + return s; + } + + private static boolean getBoolean(Map<String,Object> map, String name) { + Boolean b = (Boolean)map.get(name); + if (b == null) return false; + return b.booleanValue(); + } + + @SuppressWarnings("unchecked") + private CodeArea[] toTextAreaArray(Object o) { + if (o == null) return null; + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + int n = 0; + CodeArea[] arr = new CodeArea[c.size()]; + String directory = null; + String file = null; + for (Map<String,Object> area : c) { + directory = getString(area, "Dir", directory); + file = getString(area, "File", file); + arr[n++] = new CodeArea(directory, file, + getInteger(area, "SLine", 0), getInteger(area, "SCol", 0), + getInteger(area, "ELine", 0), getInteger(area, "ECol", 0), + (Number)area.get("SAddr"), (Number)area.get("EAddr"), + getInteger(area, "ISA", 0), + getBoolean(area, "IsStmt"), getBoolean(area, "BasicBlock"), + getBoolean(area, "PrologueEnd"), getBoolean(area, "EpilogueBegin")); + } + return arr; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LocatorProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LocatorProxy.java new file mode 100644 index 000000000..91dccfb38 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/LocatorProxy.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ILocator; + +public class LocatorProxy implements ILocator { + + private final IChannel channel; + private final Map<String,IPeer> peers = new HashMap<String,IPeer>(); + private final Collection<LocatorListener> listeners = new ArrayList<LocatorListener>(); + + private class Peer implements IPeer { + + private final Map<String, String> attrs; + + Peer(Map<String,String> attrs) { + this.attrs = attrs; + } + + public Map<String, String> getAttributes() { + assert Protocol.isDispatchThread(); + return attrs; + } + + public String getID() { + assert Protocol.isDispatchThread(); + return attrs.get(ATTR_ID); + } + + public String getName() { + assert Protocol.isDispatchThread(); + return attrs.get(ATTR_NAME); + } + + public String getOSName() { + assert Protocol.isDispatchThread(); + return attrs.get(ATTR_OS_NAME); + } + + public String getTransportName() { + assert Protocol.isDispatchThread(); + return attrs.get(ATTR_TRANSPORT_NAME); + } + + public IChannel openChannel() { + assert Protocol.isDispatchThread(); + IChannel c = channel.getRemotePeer().openChannel(); + c.redirect(getID()); + return c; + } + }; + + private final IChannel.IEventListener event_listener = new IChannel.IEventListener() { + + @SuppressWarnings("unchecked") + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("peerAdded")) { + assert args.length == 1; + IPeer peer = new Peer((Map<String,String>)args[0]); + peers.put(peer.getID(), peer); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext();) { + i.next().peerAdded(peer); + } + } + else if (name.equals("peerChanged")) { + assert args.length == 1; + Map<String,String> m = (Map<String,String>)args[0]; + if (m == null) throw new Error("Locator service: invalid peerChanged event - no peer ID"); + IPeer peer = peers.get(m.get(IPeer.ATTR_ID)); + if (peer == null) throw new Error("Invalid peerChanged event: unknown peer ID"); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext();) { + i.next().peerChanged(peer); + } + } + else if (name.equals("peerRemoved")) { + assert args.length == 1; + String id = (String)args[0]; + IPeer peer = peers.get(id); + if (peer == null) throw new Error("Locator service: invalid peerRemoved event - unknown peer ID"); + for (Iterator<LocatorListener> i = listeners.iterator(); i.hasNext();) { + i.next().peerRemoved(id); + } + } + else { + throw new IOException("Locator service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + + public LocatorProxy(IChannel channel) { + this.channel = channel; + channel.addEventListener(this, event_listener); + } + + public String getName() { + return NAME; + } + + public Map<String,IPeer> getPeers() { + return peers; + } + + public IToken redirect(String peer_id, final DoneRedirect done) { + return new Command(channel, this, "redirect", new Object[]{ peer_id }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneRedirect(token, error); + } + }.token; + } + + public IToken sync(final DoneSync done) { + return new Command(channel, this, "sync", null) { + @Override + public void done(Exception error, Object[] args) { + if (error != null) channel.terminate(error); + done.doneSync(token); + } + }.token; + } + + public void addListener(LocatorListener listener) { + listeners.add(listener); + } + + public void removeListener(LocatorListener listener) { + listeners.remove(listener); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/MemoryProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/MemoryProxy.java new file mode 100644 index 000000000..19b3e1aa4 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/MemoryProxy.java @@ -0,0 +1,366 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.windriver.tcf.api.core.Base64; +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.internal.core.ReadOnlyMap; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.services.IMemory; + +public class MemoryProxy implements IMemory { + + private final IChannel channel; + private final Map<MemoryListener,IChannel.IEventListener> listeners = + new HashMap<MemoryListener,IChannel.IEventListener>(); + + private static class Range implements Comparable<Range> { + int offs; + int size; + int stat; + String msg; + + public int compareTo(Range o) { + if (offs < o.offs) return -1; + if (offs > o.offs) return +1; + return 0; + } + } + + private class MemoryErrorReport extends MemoryError implements ErrorOffset { + + private static final long serialVersionUID = 796525409870265390L; + private final Range[] ranges; + + @SuppressWarnings("unchecked") + MemoryErrorReport(String msg, Number addr, Object ranges) { + super(msg); + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)ranges; + this.ranges = new Range[c.size()]; + int n = 0; + BigInteger addr_bi = addr instanceof BigInteger ? + (BigInteger)addr : new BigInteger(addr.toString()); + for (Map<String,Object> m : c) { + Range r = new Range(); + Number x = (Number)m.get("addr"); + BigInteger y = x instanceof BigInteger ? + (BigInteger)x : new BigInteger(x.toString()); + r.offs = addr_bi.subtract(y).intValue(); + r.size = ((Number)m.get("size")).intValue(); + r.stat = ((Number)m.get("stat")).intValue(); + r.msg = Command.toErrorString(m.get("msg")); + assert r.offs >= 0; + assert r.size >= 0; + this.ranges[n++] = r; + } + Arrays.sort(this.ranges); + } + + public String getMessage(int offset) { + int l = 0; + int h = ranges.length - 1; + while (l <= h) { + int n = (l + h) / 2; + Range r = ranges[n]; + if (r.offs > offset) { + h = n - 1; + } + else if (offset >= r.offs + r.size) { + l = n + 1; + } + else { + return r.msg; + } + } + return null; + } + + public int getStatus(int offset) { + int l = 0; + int h = ranges.length - 1; + while (l <= h) { + int n = (l + h) / 2; + Range r = ranges[n]; + if (r.offs > offset) { + h = n - 1; + } + else if (offset >= r.offs + r.size) { + l = n + 1; + } + else { + return r.stat; + } + } + return BYTE_UNKNOWN; + } + } + + private class MemContext implements MemoryContext { + + private final Map<String,Object> props; + + MemContext(Map<String,Object> props) { + assert props instanceof ReadOnlyMap; + this.props = props; + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getParentID() { + String s = (String)props.get(PROP_PARENT_ID); + if (s == null) return ""; + return s; + } + + public int getAddressSize() { + Number n = (Number)props.get(PROP_ADDRESS_SIZE); + if (n == null) return 0; + return n.intValue(); + } + + public int getProcessID() { + Number n = (Number)props.get(PROP_PROCESS_ID); + if (n == null) return 0; + return n.intValue(); + } + + public boolean isBigEndian() { + Boolean n = (Boolean)props.get(PROP_BIG_ENDIAN); + if (n == null) return false; + return n.booleanValue(); + } + + public Map<String, Object> getProperties() { + return props; + } + + public IToken fill(final Number addr, int word_size, + byte[] value, int size, int mode, final DoneMemory done) { + return new MemoryCommand("fill", new Object[] { + getID(), addr, word_size, size, mode, value + } ) { + public void done(Exception error, Object[] args) { + MemoryError e = null; + if (error != null) { + e = new MemoryError(error.getMessage()); + } + else { + assert args.length == 3; + e = toMemoryError(addr, args[0], args[1], args[2]); + } + done.doneMemory(token, e); + } + }.token; + } + + public IToken get(final Number addr, int word_size, + final byte[] buf, final int offs, final int size, + int mode, final DoneMemory done) { + return new MemoryCommand("get", new Object[] { + getID(), addr, word_size, size, mode + } ) { + public void done(Exception error, Object[] args) { + MemoryError e = null; + if (error != null) { + e = new MemoryError(error.getMessage()); + } + else { + assert args.length == 4; + String str = (String)args[0]; + if (str != null) Base64.toByteArray(buf, offs, size, str.toCharArray()); + e = toMemoryError(addr, args[1], args[2], args[3]); + } + done.doneMemory(token, e); + } + }.token; + } + + public IToken set(final Number addr, int word_size, + byte[] buf, int offs, int size, int mode, final DoneMemory done) { + return new MemoryCommand("set", new Object[] { + getID(), addr, word_size, size, mode, Base64.toBase64(buf, offs, size) + } ) { + public void done(Exception error, Object[] args) { + MemoryError e = null; + if (error != null) { + e = new MemoryError(error.getMessage()); + } + else { + assert args.length == 3; + e = toMemoryError(addr, args[0], args[1], args[2]); + } + done.doneMemory(token, e); + } + }.token; + } + + public String toString() { + return "[Memory Context " + props.toString() + "]"; + } + } + + public MemoryProxy(IChannel channel) { + this.channel = channel; + } + + public void addListener(final MemoryListener listener) { + IChannel.IEventListener l = new IChannel.IEventListener() { + + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("contextAdded")) { + assert args.length == 1; + listener.contextAdded(toContextArray(args[0])); + } + else if (name.equals("contextChanged")) { + assert args.length == 1; + listener.contextChanged(toContextArray(args[0])); + } + else if (name.equals("contextRemoved")) { + assert args.length == 1; + listener.contextRemoved(toStringArray(args[0])); + } + else if (name.equals("memoryChanged")) { + assert args.length == 2; + listener.memoryChanged((String)args[0], + toAddrArray(args[1]), toSizeArray(args[1])); + } + else { + throw new IOException("Memory service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + channel.addEventListener(this, l); + listeners.put(listener, l); + } + + public void removeListener(MemoryListener listener) { + IChannel.IEventListener l = listeners.remove(listener); + if (l != null) channel.removeEventListener(this, l); + } + + public IToken getContext(String context_id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ context_id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + MemContext ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new MemContext((Map<String,Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + public String getName() { + return NAME; + } + + private abstract class MemoryCommand extends Command { + + MemoryCommand(String cmd, Object[] args) { + super(channel, MemoryProxy.this, cmd, args); + } + + MemoryError toMemoryError(Number addr, Object code, Object data, Object ranges) { + int error_code = ((Number)code).intValue(); + if (error_code == 0) return null; + String cmd = getCommandString(); + if (cmd.length() > 72) cmd = cmd.substring(0, 72) + "..."; + return new MemoryErrorReport( + "TCF command exception:" + + "\nCommand: " + cmd + + "\nException: " + toErrorString(data) + + "\nError code: " + code, addr, ranges); + } + } + + @SuppressWarnings("unchecked") + private MemoryContext[] toContextArray(Object o) { + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + if (c == null) return new MemoryContext[0]; + int n = 0; + MemoryContext[] ctx = new MemoryContext[c.size()]; + for (Iterator<Map<String,Object>> i = c.iterator(); i.hasNext();) { + ctx[n++] = new MemContext(i.next()); + } + return ctx; + } + + @SuppressWarnings("unchecked") + private long[] toSizeArray(Object o) { + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + if (c == null) return null; + long[] a = new long[c.size()]; + int n = 0; + for (Map<String,Object> m : c) { + Number sz = (Number)m.get("size"); + a[n++] = sz == null ? 0 : sz.longValue(); + } + return a; + } + + @SuppressWarnings("unchecked") + private Number[] toAddrArray(Object o) { + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + if (c == null) return null; + Number[] a = new Number[c.size()]; + int n = 0; + for (Map<String,Object> m : c) { + a[n++] = (Number)m.get("addr"); + } + return a; + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/ProcessesProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/ProcessesProxy.java new file mode 100644 index 000000000..4db81a295 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/ProcessesProxy.java @@ -0,0 +1,232 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IProcesses; + +public class ProcessesProxy implements IProcesses { + + private final IChannel channel; + + private class ProcessContext implements IProcesses.ProcessContext { + + private final Map<String,Object> props; + + ProcessContext(Map<String,Object> props) { + this.props = props; + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getParentID() { + return (String)props.get(PROP_PARENTID); + } + + public boolean canTerminate() { + Boolean b = (Boolean)props.get(PROP_CAN_TERMINATE); + return b != null && b.booleanValue(); + } + + public String getName() { + return (String)props.get(PROP_NAME); + } + + public boolean isAttached() { + Boolean b = (Boolean)props.get(PROP_ATTACHED); + return b != null && b.booleanValue(); + } + + public IToken attach(final DoneCommand done) { + return new Command(channel, ProcessesProxy.this, + "attach", new Object[]{ getID() }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken detach(final DoneCommand done) { + return new Command(channel, ProcessesProxy.this, + "detach", new Object[]{ getID() }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken signal(int signal, final DoneCommand done) { + return new Command(channel, ProcessesProxy.this, + "signal", new Object[]{ getID(), signal }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public IToken terminate(final DoneCommand done) { + return new Command(channel, ProcessesProxy.this, + "terminate", new Object[]{ getID() }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public Map<String, Object> getProperties() { + return props; + } + + public String toString() { + return "[Processes Context " + props.toString() + "]"; + } + } + + public ProcessesProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken getChildren(String parent_context_id, boolean attached_only, final DoneGetChildren done) { + return new Command(channel, this, + "getChildren", new Object[]{ parent_context_id, attached_only }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] ids = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + ids = toStringArray(args[2]); + } + done.doneGetChildren(token, error, ids); + } + }.token; + } + + public IToken getContext(String id, final DoneGetContext done) { + return new Command(channel, this, + "getContext", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + ProcessContext ctx = null; + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new ProcessContext((Map<String, Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public IToken getEnvironment(final DoneGetEnvironment done) { + return new Command(channel, this, "getEnvironment", null) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + Map<String,String> env = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + env = toEnvMap(args[2]); + } + done.doneGetEnvironment(token, error, env); + } + }.token; + } + + public IToken start(String directory, String file, + String[] command_line, Map<String,String> environment, + boolean attach, final DoneStart done) { + return new Command(channel, this, + "start", new Object[]{ directory, file, command_line, + toEnvStringArray(environment), attach }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + ProcessContext ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new ProcessContext((Map<String, Object>)args[2]); + } + } + done.doneStart(token, error, ctx); + } + }.token; + } + + @SuppressWarnings("unchecked") + private static String[] toStringArray(Object o) { + if (o == null) return new String[0]; + Collection<String> c = (Collection<String>)o; + return (String[])c.toArray(new String[c.size()]); + } + + private static String[] toEnvStringArray(Map<String,String> m) { + if (m == null) return new String[0]; + int n = 0; + String[] arr = new String[m.size()]; + for (String s : m.keySet()) { + arr[n++] = s + "=" + m.get(s); + } + return arr; + } + + @SuppressWarnings("unchecked") + private static Map<String,String> toEnvMap(Object o) { + Map<String,String> m = new HashMap<String,String>(); + if (o == null) return m; + Collection<String> c = (Collection<String>)o; + for (String s : c) { + int i = s.indexOf('='); + if (i >= 0) m.put(s.substring(0, i), s.substring(i + 1)); + else m.put(s, ""); + } + return m; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RegistersProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RegistersProxy.java new file mode 100644 index 000000000..e313078ec --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RegistersProxy.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.services.IRegisters; + +public class RegistersProxy implements IRegisters { + + private final IChannel channel; + private final Map<RegistersListener,IChannel.IEventListener> listeners = + new HashMap<RegistersListener,IChannel.IEventListener>(); + + private class Context implements RegistersContext { + + private final Map<String,Object> props; + + Context(Map<String,Object> props) { + this.props = props; + } + + public String[] getAvailableFormats() { + return toStringArray(props.get(PROP_FORMATS)); + } + + public int[] getBitNumbers() { + return toIntArray(props.get(PROP_BITS)); + } + + public String getDescription() { + return (String)props.get(PROP_DESCRIPTION); + } + + public int getFirstBitNumber() { + Number n = (Number)props.get(PROP_FIST_BIT); + if (n == null) return 0; + return n.intValue(); + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getName() { + return (String)props.get(PROP_NAME); + } + + public NamedValue[] getNamedValues() { + return toValuesArray(props.get(PROP_VALUES)); + } + + public String getParentID() { + return (String)props.get(PROP_PARENT_ID); + } + + public Map<String, Object> getProperties() { + return props; + } + + public boolean hasSideEffects() { + Boolean n = (Boolean)props.get(PROP_SIDE_EFFECTS); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isBigEndian() { + Boolean n = (Boolean)props.get(PROP_BIG_ENDIAN); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isFloat() { + Boolean n = (Boolean)props.get(PROP_FLOAT); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isLeftToRight() { + Boolean n = (Boolean)props.get(PROP_LEFT_TO_RIGHT); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isReadOnce() { + Boolean n = (Boolean)props.get(PROP_READ_ONCE); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isReadable() { + Boolean n = (Boolean)props.get(PROP_READBLE); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isVolatile() { + Boolean n = (Boolean)props.get(PROP_VOLATILE); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isWriteOnce() { + Boolean n = (Boolean)props.get(PROP_WRITE_ONCE); + if (n == null) return false; + return n.booleanValue(); + } + + public boolean isWriteable() { + Boolean n = (Boolean)props.get(PROP_WRITEABLE); + if (n == null) return false; + return n.booleanValue(); + } + + public IToken get(String format, final DoneGet done) { + return new Command(channel, RegistersProxy.this, "get", + new Object[]{ getID(), format }) { + @Override + public void done(Exception error, Object[] args) { + String val = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + val = (String)args[2]; + } + done.doneGet(token, error, val); + } + }.token; + } + + public IToken set(String format, String value, final DoneSet done) { + return new Command(channel, RegistersProxy.this, "set", + new Object[]{ getID(), format, value }) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneSet(token, error); + } + }.token; + } + + public String toString() { + return "[Registers Context " + props.toString() + "]"; + } + } + + public RegistersProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + public IToken getContext(String id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + Context ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new Context((Map<String,Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public void addListener(final RegistersListener listener) { + IChannel.IEventListener l = new IChannel.IEventListener() { + + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("contextChanged")) { + listener.contextChanged(); + } + else if (name.equals("registerChanged")) { + assert args.length == 1; + listener.registerChanged((String)args[0]); + } + else { + throw new IOException("Registers service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + channel.addEventListener(this, l); + listeners.put(listener, l); + } + + public void removeListener(RegistersListener listener) { + IChannel.IEventListener l = listeners.remove(listener); + if (l != null) channel.removeEventListener(this, l); + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } + + @SuppressWarnings("unchecked") + private int[] toIntArray(Object o) { + Collection<Number> c = (Collection<Number>)o; + if (c == null) return null; + int i = 0; + int[] arr = new int[c.size()]; + for (Number n : c) arr[i++] = n.intValue(); + return arr; + } + + @SuppressWarnings("unchecked") + private NamedValue[] toValuesArray(Object o) { + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + if (c == null) return null; + int i = 0; + NamedValue[] arr = new NamedValue[c.size()]; + for (final Map<String,Object> m : c) { + arr[i++] = new NamedValue() { + + public String getDescription() { + return (String)m.get("Description"); + } + + public String getName() { + return (String)m.get("Name"); + } + + public Number getValue() { + return (Number)m.get("Value"); + } + }; + } + return arr; + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RunControlProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RunControlProxy.java new file mode 100644 index 000000000..774a5e6ec --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/RunControlProxy.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.internal.core.ReadOnlyMap; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.JSON; +import com.windriver.tcf.api.services.IRunControl; + +public class RunControlProxy implements IRunControl { + + private final IChannel channel; + private final Map<RunControlListener,IChannel.IEventListener> listeners = + new HashMap<RunControlListener,IChannel.IEventListener>(); + + private class RunContext implements IRunControl.RunControlContext { + + private final Map<String, Object> props; + + RunContext(Map<String, Object> props) { + assert props instanceof ReadOnlyMap; + this.props = props; + } + + public Map<String, Object> getProperties() { + return props; + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getParentID() { + String s = (String)props.get(PROP_PARENT_ID); + if (s == null) return ""; + return s; + } + + public boolean isContainer() { + Boolean b = (Boolean)props.get(PROP_IS_CONTAINER); + return b != null && b.booleanValue(); + } + + public boolean hasState() { + Boolean b = (Boolean)props.get(PROP_HAS_STATE); + return b != null && b.booleanValue(); + } + + public boolean canResume(int mode) { + if (props.containsKey(PROP_CAN_RESUME)) { + int b = ((Number)props.get(PROP_CAN_RESUME)).intValue(); + return (b & (1 << mode)) != 0; + } + return false; + } + + public boolean canCount(int mode) { + if (props.containsKey(PROP_CAN_COUNT)) { + int b = ((Number)props.get(PROP_CAN_COUNT)).intValue(); + return (b & (1 << mode)) != 0; + } + return false; + } + + public boolean canSuspend() { + Boolean b = (Boolean)props.get(PROP_CAN_SUSPEND); + return b != null && b.booleanValue(); + } + + public boolean canTerminate() { + Boolean b = (Boolean)props.get(PROP_CAN_TERMINATE); + return b != null && b.booleanValue(); + } + + public IToken getState(final DoneGetState done) { + return new Command(channel, RunControlProxy.this, "getState", new Object[]{ getID() }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + boolean susp = false; + String pc = null; + String reason = null; + Map<String,Object> map = null; + if (error == null) { + assert args.length == 6; + error = toError(args[0], args[1]); + susp = ((Boolean)args[2]).booleanValue(); + if (args[3] != null) pc = ((Number)args[3]).toString(); + reason = (String)args[4]; + map = (Map<String,Object>)args[5]; + } + done.doneGetState(token, error, susp, pc, reason, map); + } + }.token; + } + + public IToken resume(int mode, int count, DoneCommand done) { + return command("resume", new Object[]{ getID(), mode, count }, done); + } + + public IToken suspend(DoneCommand done) { + return command("suspend", new Object[]{ getID() }, done); + } + + public IToken terminate(DoneCommand done) { + return command("terminate", new Object[]{ getID() }, done); + } + + private IToken command(String cmd, Object[] args, final DoneCommand done) { + return new Command(channel, RunControlProxy.this, cmd, args) { + @Override + public void done(Exception error, Object[] args) { + if (error == null) { + assert args.length == 2; + error = toError(args[0], args[1]); + } + done.doneCommand(token, error); + } + }.token; + } + + public String toString() { + return "[Run Control Context " + props.toString() + "]"; + } + } + + public RunControlProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public void addListener(final RunControlListener listener) { + IChannel.IEventListener l = new IChannel.IEventListener() { + + @SuppressWarnings("unchecked") + public void event(String name, byte[] data) { + try { + Object[] args = JSON.parseSequence(data); + if (name.equals("contextSuspended")) { + assert args.length == 4; + listener.contextSuspended( + (String)args[0], + args[1] == null ? null : ((Number)args[1]).toString(), + (String)args[2], (Map<String,Object>)args[3]); + } + else if (name.equals("contextResumed")) { + assert args.length == 1; + listener.contextResumed((String)args[0]); + } + else if (name.equals("contextAdded")) { + assert args.length == 1; + listener.contextAdded(toContextArray(args[0])); + } + else if (name.equals("contextChanged")) { + assert args.length == 1; + listener.contextChanged(toContextArray(args[0])); + } + else if (name.equals("contextRemoved")) { + assert args.length == 1; + listener.contextRemoved(toStringArray(args[0])); + } + else if (name.equals("contextException")) { + assert args.length == 2; + listener.contextException((String)args[0], (String)args[1]); + } + else if (name.equals("containerSuspended")) { + assert args.length == 5; + listener.containerSuspended( + (String)args[0], + args[1] == null ? null : ((Number)args[1]).toString(), + (String)args[2], (Map)args[3], + toStringArray(args[4])); + } + else if (name.equals("containerResumed")) { + assert args.length == 1; + listener.containerResumed(toStringArray(args[0])); + } + else { + throw new IOException("RunControl service: unknown event: " + name); + } + } + catch (Throwable x) { + channel.terminate(x); + } + } + }; + channel.addEventListener(this, l); + listeners.put(listener, l); + } + + public void removeListener(RunControlListener listener) { + IChannel.IEventListener l = listeners.remove(listener); + if (l != null) channel.removeEventListener(this, l); + } + + public IToken getContext(String context_id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ context_id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + RunControlContext ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new RunContext((Map<String, Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + @SuppressWarnings("unchecked") + private RunControlContext[] toContextArray(Object o) { + if (o == null) return null; + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + int n = 0; + RunControlContext[] ctx = new RunControlContext[c.size()]; + for (Map<String, Object> m : c) ctx[n++] = new RunContext(m); + return ctx; + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/StackTraceProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/StackTraceProxy.java new file mode 100644 index 000000000..5385c5d1f --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/StackTraceProxy.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IStackTrace; + +public class StackTraceProxy implements IStackTrace { + + private final IChannel channel; + + private class Context implements StackTraceContext { + + private final Map<String,Object> props; + + Context(Map<String,Object> props) { + this.props = props; + } + + public Number getArgumentsAddress() { + return (Number)props.get(PROP_ARGUMENTS_ADDRESS); + } + + public int getArgumentsCount() { + Number n = (Number)props.get(PROP_ARGUMENTS_COUNT); + if (n == null) return 0; + return n.intValue(); + } + + public Number getFrameAddress() { + return (Number)props.get(PROP_FRAME_ADDRESS); + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getName() { + return (String)props.get(PROP_NAME); + } + + public String getParentID() { + return (String)props.get(PROP_PARENT_ID); + } + + public Number getReturnAddress() { + return (Number)props.get(PROP_RETURN_ADDRESS); + } + + public Map<String, Object> getProperties() { + return props; + } + } + + public StackTraceProxy(IChannel channel) { + this.channel = channel; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray(args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + public IToken getContext(String[] id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + StackTraceContext[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[1], args[2]); + arr = toContextArray(args[0]); + } + done.doneGetContext(token, error, arr); + } + }.token; + } + + public String getName() { + return NAME; + } + + @SuppressWarnings("unchecked") + private StackTraceContext[] toContextArray(Object o) { + if (o == null) return null; + Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; + int n = 0; + StackTraceContext[] ctx = new StackTraceContext[c.size()]; + for (Map<String,Object> m : c) ctx[n++] = new Context(m); + return ctx; + } + + @SuppressWarnings("unchecked") + private String[] toStringArray(Object o) { + Collection<String> c = (Collection<String>)o; + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/SysMonitorProxy.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/SysMonitorProxy.java new file mode 100644 index 000000000..6e4648bc8 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/internal/services/remote/SysMonitorProxy.java @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.internal.services.remote; + +import java.util.Collection; +import java.util.Map; + +import com.windriver.tcf.api.core.Command; +import com.windriver.tcf.api.internal.core.ReadOnlyMap; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.ISysMonitor; + +public class SysMonitorProxy implements ISysMonitor { + + private final IChannel channel; + + private class SysMonitorContext implements ISysMonitor.SysMonitorContext { + + private final Map<String, Object> props; + + SysMonitorContext(Map<String, Object> props) { + assert props instanceof ReadOnlyMap; + this.props = props; + } + + public String getID() { + return (String)props.get(PROP_ID); + } + + public String getCurrentWorkingDirectory() { + return (String)props.get(PROP_CWD); + } + + public String getFile() { + return (String)props.get(PROP_FILE); + } + + public String getParentID() { + return (String)props.get(PROP_PARENTID); + } + + public String getRoot() { + return (String)props.get(PROP_ROOT); + } + + public String getGroupName() { + return (String)props.get(PROP_GROUPNAME); + } + + public long getPGRP() { + if (!props.containsKey(PROP_PGRP)) return -1; + return ((Number)props.get(PROP_PGRP)).longValue(); + } + + public long getPID() { + if (!props.containsKey(PROP_PID)) return -1; + return ((Number)props.get(PROP_PID)).longValue(); + } + + public long getPPID() { + if (!props.containsKey(PROP_PPID)) return -1; + return ((Number)props.get(PROP_PPID)).longValue(); + } + + public long getRSS() { + if (!props.containsKey(PROP_RSS)) return -1; + return ((Number)props.get(PROP_RSS)).longValue(); + } + + public String getState() { + return (String)props.get(PROP_STATE); + } + + public long getTGID() { + if (!props.containsKey(PROP_TGID)) return -1; + return ((Number)props.get(PROP_TGID)).longValue(); + } + + public long getTracerPID() { + if (!props.containsKey(PROP_TRACERPID)) return -1; + return ((Number)props.get(PROP_TRACERPID)).longValue(); + } + + public long getUGID() { + if (!props.containsKey(PROP_UGID)) return -1; + return ((Number)props.get(PROP_UGID)).longValue(); + } + + public long getUID() { + if (!props.containsKey(PROP_UID)) return -1; + return ((Number)props.get(PROP_UID)).longValue(); + } + + public String getUserName() { + return (String)props.get(PROP_USERNAME); + } + + public long getVSize() { + if (!props.containsKey(PROP_VSIZE)) return -1; + return ((Number)props.get(PROP_VSIZE)).longValue(); + } + + public long getPSize() { + if (!props.containsKey(PROP_PSIZE)) return -1; + return ((Number)props.get(PROP_PSIZE)).longValue(); + } + + public Map<String, Object> getProperties() { + return props; + } + + public String toString() { + return "[Sys Monitor Context " + props.toString() + "]"; + } + } + + public SysMonitorProxy(IChannel channel) { + this.channel = channel; + } + + public String getName() { + return NAME; + } + + public IToken getChildren(String parent_context_id, final DoneGetChildren done) { + return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray((Collection<String>)args[2]); + } + done.doneGetChildren(token, error, arr); + } + }.token; + } + + public IToken getContext(String id, final DoneGetContext done) { + return new Command(channel, this, "getContext", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + SysMonitorContext ctx = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + if (args[2] != null) { + ctx = new SysMonitorContext((Map<String, Object>)args[2]); + } + } + done.doneGetContext(token, error, ctx); + } + }.token; + } + + public IToken getCommandLine(String id, final DoneGetCommandLine done) { + return new Command(channel, this, "getCommandLine", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray((Collection<String>)args[2]); + } + done.doneGetCommandLine(token, error, arr); + } + }.token; + } + + public IToken getEnvironment(String id, final DoneGetEnvironment done) { + return new Command(channel, this, "getEnvironment", new Object[]{ id }) { + @SuppressWarnings("unchecked") + @Override + public void done(Exception error, Object[] args) { + String[] arr = null; + if (error == null) { + assert args.length == 3; + error = toError(args[0], args[1]); + arr = toStringArray((Collection<String>)args[2]); + } + done.doneGetEnvironment(token, error, arr); + } + }.token; + } + + private static String[] toStringArray(Collection<String> c) { + if (c == null) return new String[0]; + return (String[])c.toArray(new String[c.size()]); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IChannel.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IChannel.java new file mode 100644 index 000000000..4b15660b8 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IChannel.java @@ -0,0 +1,261 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.protocol; + +import java.util.Collection; + +/** + * IChannel represents communication link connecting two end points (peers). + * The channel asynchronously transmits messages: commands, results and events. + * A single channel may be used to communicate with multiple services. + * Multiple channels may be used to connect the same peers, however no command or event + * ordering is guaranteed across channels. + */ + +public interface IChannel { + + /** + * Channel state IDs + */ + static final int + STATE_OPENNING = 0, + STATE_OPEN = 1, + STATE_CLOSED = 2; + + /** + * @return channel state, see STATE_* + */ + int getState(); + + /** + * Send command message to remote peer for execution. Commands can be queued + * locally before transmission. Sending commands too fast can fill up + * communication channel buffers. Calling thread will be blocked until + * enough buffer space is freed up by transmitting pending messages. + * @param service - a remote service that will be sent the command + * @param name - command name + * @param args - command arguments encoded into array of bytes + * @param done - call back object + * @return pending command handle + */ + IToken sendCommand(IService service, String name, byte[] args, ICommandListener done); + + /** + * Command listener interface. Clients implement this interface to + * receive command results. + */ + interface ICommandListener { + + /** + * Called when progress message (intermediate result) is received + * from remote peer. + * @param token - command handle + * @param data - progress message arguments encoded into array of bytes + */ + void progress(IToken token, byte[] data); + + /** + * Called when command result received from remote peer. + * @param token - command handle + * @param data - command result message arguments encoded into array of bytes + */ + void result(IToken token, byte[] data); + + /** + * Called when communication channel was closed while command was waiting for result. + * @param token - command handle + * @param error - exception that forced the channel to close + */ + void terminated(IToken token, Exception error); + } + + /** + * Send result message to remote peer. Messages can be queued locally before + * transmission. Sending messages too fast can fill up communication channel + * buffers. Calling thread will be blocked until enough buffer space is + * freed up by transmitting pending messages. + * @param token - command handle + * @param results - result message arguments encoded into array of bytes + */ + void sendResult(IToken token, byte[] results); + + /** + * Get current level of outbound traffic congestion. + * + * @return integer value in range –100..100, where –100 means no pending + * messages (no traffic), 0 means optimal load, and positive numbers + * indicate level of congestion. + * + * Note: inbound traffic congestion is detected by framework and reported to + * remote peer without client needed to be involved. + */ + int getCongestion(); + + /** + * Channel listener interface. + */ + interface IChannelListener { + + /** + * Called when a channel is opened. + */ + void onChannelOpened(); + + /** + * Called when channel closed. If it is closed because of an error, + * ‘error’ parameter will describe the error. ‘error’ is null if channel + * is closed normally by calling Channel.close(). + * @param error - channel exception or null + */ + void onChannelClosed(Throwable error); + + /** + * Notifies listeners about channel congestion level changes. + * When level > 0 client should delay sending more messages. + * @param level - current congestion level + */ + void congestionLevel(int level); + } + + /** + * Subscribe a channel listener. The listener will be notified about changes of + * channel state and changes of outbound traffic congestion level. + * @param listener - channel listener implementation + */ + void addChannelListener(IChannelListener listener); + + /** + * Remove a channel listener. + * @param listener - channel listener implementation + */ + void removeChannelListener(IChannelListener listener); + + /** + * Command server interface. + * This interface is to be implemented by service providers. + */ + interface ICommandServer { + + /** + * Called every time a command is received from remote peer. + * @param token - command handle + * @param name - command name + * @param data - command arguments encoded into array of bytes + */ + void command(IToken token, String name, byte[] data); + } + + /** + * Subscribe a command server. The server will be notified about command + * messages received through this channel for given service. + * @param service - local service implementation + * @param server - implementation of service commands listener + */ + void addCommandServer(IService service, ICommandServer server); + + /** + * Remove a command server. + * @param service - local service implementation + * @param server - implementation of service commands listener + */ + void removeCommandServer(IService service, ICommandServer server); + + /** + * A generic interface for service event listener. + * Services usually define a service specific event listener interface. + * Clients should user service specific listener interface, unless no such interface is defined. + */ + interface IEventListener { + /** + * Called when service event message is received + * @param name - event name + * @param data - event arguments encode as array of bytes + */ + void event(String name, byte[] data); + } + + /** + * Subscribe an event listener for given service. + * @param service - remote service proxy + * @param server - implementation of service event listener + */ + void addEventListener(IService service, IEventListener listener); + + /** + * Unsubscribe an event listener for given service. + * @param service - remote service proxy + * @param server - implementation of service event listener + */ + void removeEventListener(IService service, IEventListener listener); + + /** + * @return IPeer object representing local endpoint of communication channel. + */ + IPeer getLocalPeer(); + + /** + * @return IPeer object representing remote endpoint of communication channel. + */ + IPeer getRemotePeer(); + + /** + * @return collection of services available on local peer. + */ + Collection<String> getLocalServices(); + + /** + * @return an object representing a service from local peer. + * Return null if the service is not available. + */ + IService getLocalService(String service_name); + + /** + * @return an object representing a service from local peer. + * Service object should implement given interface. + * Return null if implementation of the interface is not available. + */ + <V extends IService> V getLocalService(Class<V> service_interface); + + /** + * @return collection of services available on remote peer. + */ + Collection<String> getRemoteServices(); + + /** + * @return an object representing a service from remote peer. + * Return null if the service is not available. + */ + IService getRemoteService(String service_name); + + /** + * @return an object representing a service from remote peer. + * Service object should implement given interface. + * Return null if implementation of the interface is not available. + */ + <V extends IService> V getRemoteService(Class<V> service_interface); + + /** + * Close communication channel. + */ + void close(); + + /** + * Close channel in case of communication error. + * @param error - cause of channel termination + */ + void terminate(Throwable error); + + /** + * Redirect this channel to given peer using this channel remote peer locator service as a proxy. + * @param peer_id - peer that will become new remote communication endpoint of this channel + */ + void redirect(String peer_id); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IEventQueue.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IEventQueue.java new file mode 100644 index 000000000..0ace6e635 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IEventQueue.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.protocol; + +/** + * Clients of the framework should implement this interface and call Protocol.setEventQueue. + * Implementation should encapsulate a queue and asynchronous event dispatch machinery, which + * extracts events from the queue and dispatches them by calling event's run() method. + * The implementation is used by framework to queue and dispatch all events. + */ +public interface IEventQueue { + + /** + * Causes <code>runnable</code> to have its <code>run</code> + * method called in the dispatch thread of this event queue. + * Events are dispatched in same order as queued. + * + * @param runnable the <code>Runnable</code> whose <code>run</code> + * method should be executed asynchronously. + */ + void invokeLater(Runnable runnable); + + /** + * Returns true if the calling thread is this event queue's dispatch thread. + * Use this call the ensure that a given task is being executed (or not being) on dispatch thread. + * + * @return true if running on the dispatch thread. + */ + boolean isDispatchThread(); + + /** + * Get current level of queue congestion. + * + * @return integer value in range –100..100, where –100 means no pending + * messages (no traffic), 0 means optimal load, and positive numbers + * indicate level of congestion. + */ + public int getCongestion(); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IPeer.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IPeer.java new file mode 100644 index 000000000..7abc75e2c --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IPeer.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.protocol; + +import java.util.Map; + +/** + * Both hosts and targets are represented by objects + * implementing IPeer interface. A peer can act as host or + * target depending on services it implements. + * List of currently known peers can be retrieved by + * calling ILocator.getPeers() + */ +public interface IPeer { + + /** + * Peer property names. Implementation can define additional properties. + */ + static final String + ATTR_ID = "ID", + ATTR_NAME = "Name", + ATTR_OS_NAME = "OSName", + ATTR_TRANSPORT_NAME = "TransportName", + ATTR_IP_HOST = "Host", + ATTR_IP_ALIASES = "Aliases", + ATTR_IP_ADDRESSES = "Addresses", + ATTR_IP_PORT = "Port"; + + + /** + * @return map of peer attributes + */ + Map<String, String> getAttributes(); + + /** + * @return peer unique ID, same as getAttributes().get(ATTR_ID) + */ + String getID(); + + /** + * @return peer name, same as getAttributes().get(ATTR_NAME) + */ + String getName(); + + /** + * Same as getAttributes().get(ATTR_OS_NAME) + */ + String getOSName(); + + /** + * Same as getAttributes().get(ATTR_TRANSPORT_NAME) + */ + String getTransportName(); + + /** + * Open channel to communicate with this peer. + * Note: the channel is not fully open yet when this method returns. + * It’s state is IChannel.STATE_OPENNING. + * Protocol.Listener will be called when the channel will be opened or closed. + */ + IChannel openChannel(); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IService.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IService.java new file mode 100644 index 000000000..d6eb3d4f1 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IService.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.protocol; + +/** + * Base interface for all service interfaces. A client can get list of available services + * by calling Peer.getServices() + */ + +public interface IService { + + /** + * Get unique name of this service. + * @return service name. + */ + String getName(); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IToken.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IToken.java new file mode 100644 index 000000000..b42645107 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/IToken.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.protocol; + +/** + * IToken is created by the framework for each command sent to a remote peer. + * It is used to match results to commands and to cancel pending commands. + */ +public interface IToken { + + /** + * Try to cancel a command associated with given token. A command can be + * canceled by this method only if it was not transmitted yet to remote peer + * for execution. Successfully canceled command does not produce any result + * messages. + * + * @return true if successful. + */ + boolean cancel(); + +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/JSON.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/JSON.java new file mode 100644 index 000000000..16fdcb5e8 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/JSON.java @@ -0,0 +1,516 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.protocol; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.windriver.tcf.api.internal.core.ReadOnlyCollection; +import com.windriver.tcf.api.internal.core.ReadOnlyMap; + +/** + * JSON is TCF preferred marshaling. This class implements generation and parsing of JSON strings. + * The code is optimized for speed since it is a time-critical part of the framework. + */ +public class JSON { + + private static char[] tmp_buf = new char[0x1000]; + private static byte[] tmp_bbf = new byte[0x1000]; + private static int tmp_buf_pos; + + private static Reader reader; + private static final char[] cur_buf = new char[0x1000]; + private static int cur_buf_pos; + private static int cur_buf_len; + private static int cur_ch; + + // This buffer is used to create nice error reports + private static final char[] err_buf = new char[100]; + private static int err_buf_pos; + private static int err_buf_cnt; + + private static void write(char ch) { + if (tmp_buf_pos >= tmp_buf.length) { + char[] tmp = new char[tmp_buf.length * 2]; + System.arraycopy(tmp_buf, 0, tmp, 0, tmp_buf_pos); + tmp_buf = tmp; + } + tmp_buf[tmp_buf_pos++] = ch; + } + + private static void write(String s) { + int l = s.length(); + for (int i = 0; i < l; i++) { + char ch = s.charAt(i); + if (tmp_buf_pos >= tmp_buf.length) write(ch); + else tmp_buf[tmp_buf_pos++] = ch; + } + } + + private static void writeUInt(int n) { + assert n >= 0; + if (n >= 10) writeUInt(n / 10); + write((char)('0' + n % 10)); + } + + private static void read() throws IOException { + if (cur_buf_pos >= cur_buf_len) { + cur_buf_len = reader.read(cur_buf); + cur_buf_pos = 0; + if (cur_buf_len < 0) { + cur_buf_len = 0; + cur_ch = -1; + return; + } + } + cur_ch = cur_buf[cur_buf_pos++]; + err_buf[err_buf_pos++] = (char)cur_ch; + if (err_buf_pos >= err_buf.length) { + err_buf_pos = 0; + err_buf_cnt++; + } + } + + private static void error() throws IOException { + error("syntax error"); + } + + private static void error(String msg) throws IOException { + StringBuffer bf = new StringBuffer(); + bf.append("JSON " + msg + ":"); + int cnt = 0; + boolean nl = true; + for (int i = 0;; i++) { + char ch = 0; + if (err_buf_cnt == 0 && i < err_buf_pos) { + ch = err_buf[i]; + } + else if (err_buf_cnt > 0 && i < err_buf.length) { + ch = err_buf[(err_buf_pos + i) % err_buf.length]; + } + else { + int n = reader.read(); + if (n < 0) break; + ch = (char)n; + } + if (nl) { + bf.append("\n "); + if (err_buf_cnt == 0) bf.append(cnt); + else bf.append('*'); + bf.append(": "); + if (cnt == 0 && err_buf_cnt > 0) bf.append("..."); + nl = false; + } + if (ch == 0) { + cnt++; + nl = true; + continue; + } + bf.append(ch); + } + throw new IOException(bf.toString()); + } + + private static int readHexDigit() throws IOException { + int n = 0; + if (cur_ch >= '0' && cur_ch <= '9') n = cur_ch - '0'; + else if (cur_ch >= 'A' && cur_ch <= 'F') n = cur_ch - 'A' + 10; + else if (cur_ch >= 'a' && cur_ch <= 'f') n = cur_ch - 'a' + 10; + else error(); + read(); + return n; + } + + private static Object readNestedObject() throws IOException { + switch (cur_ch) { + case '"': + read(); + tmp_buf_pos = 0; + for (;;) { + if (cur_ch <= 0) error(); + if (cur_ch == '"') break; + if (cur_ch == '\\') { + read(); + if (cur_ch <= 0) error(); + switch (cur_ch) { + case '"': + case '\\': + case '/': + break; + case 'b': + cur_ch = '\b'; + break; + case 'f': + cur_ch = '\f'; + break; + case 'n': + cur_ch = '\n'; + break; + case 'r': + cur_ch = '\r'; + break; + case 't': + cur_ch = '\t'; + break; + case 'u': + read(); + int n = 0; + n |= readHexDigit() << 12; + n |= readHexDigit() << 8; + n |= readHexDigit() << 4; + n |= readHexDigit(); + write((char)n); + continue; + default: + error(); + break; + } + } + if (tmp_buf_pos >= tmp_buf.length) { + write((char)cur_ch); + } + else { + tmp_buf[tmp_buf_pos++] = (char)cur_ch; + } + if (cur_buf_pos >= cur_buf_len) { + read(); + } + else { + cur_ch = cur_buf[cur_buf_pos++]; + err_buf[err_buf_pos++] = (char)cur_ch; + if (err_buf_pos >= err_buf.length) { + err_buf_pos = 0; + err_buf_cnt++; + } + } + } + read(); + return new String(tmp_buf, 0, tmp_buf_pos); + case '[': + Collection<Object> l = new ArrayList<Object>(); + read(); + if (cur_ch <= 0) error(); + if (cur_ch != ']') { + for (;;) { + l.add(readNestedObject()); + if (cur_ch == ']') break; + if (cur_ch != ',') error(); + read(); + } + } + read(); + return new ReadOnlyCollection<Object>(l); + case '{': + Map<String,Object> m = new HashMap<String,Object>(); + read(); + if (cur_ch <= 0) error(); + if (cur_ch != '}') { + for (;;) { + String key = (String)readNestedObject(); + if (cur_ch != ':') error(); + read(); + Object val = readNestedObject(); + m.put(key, val); + if (cur_ch == '}') break; + if (cur_ch != ',') error(); + read(); + } + } + read(); + return new ReadOnlyMap<String,Object>(m); + case 'n': + read(); + if (cur_ch != 'u') error(); + read(); + if (cur_ch != 'l') error(); + read(); + if (cur_ch != 'l') error(); + read(); + return null; + case 'f': + read(); + if (cur_ch != 'a') error(); + read(); + if (cur_ch != 'l') error(); + read(); + if (cur_ch != 's') error(); + read(); + if (cur_ch != 'e') error(); + read(); + return Boolean.FALSE; + case 't': + read(); + if (cur_ch != 'r') error(); + read(); + if (cur_ch != 'u') error(); + read(); + if (cur_ch != 'e') error(); + read(); + return Boolean.TRUE; + default: + boolean neg = cur_ch == '-'; + if (neg) read(); + if (cur_ch >= '0' && cur_ch <= '9') { + // TODO: float number + int v = 0; + while (v <= 0x7fffffff / 10 - 1) { + v = v * 10 + (cur_ch - '0'); + read(); + if (cur_ch < '0' || cur_ch > '9') { + return new Integer(neg ? -v : v); + } + } + long vl = v; + while (vl < 0x7fffffffffffffffl / 10 - 1) { + vl = vl * 10 + (cur_ch - '0'); + read(); + if (cur_ch < '0' || cur_ch > '9') { + return new Long(neg ? -vl : vl); + } + } + StringBuffer sb = new StringBuffer(); + if (neg) sb.append('-'); + sb.append(vl); + while (true) { + sb.append(cur_ch); + read(); + if (cur_ch < '0' || cur_ch > '9') { + return new BigInteger(sb.toString()); + } + } + } + error(); + return null; + } + } + + private static Object readObject() throws IOException { + Object o = readNestedObject(); + if (cur_ch >= 0) error(); + return o; + } + + private static Object[] readSequence() throws IOException { + List<Object> l = new ArrayList<Object>(); + while (cur_ch >= 0) { + l.add(readNestedObject()); + if (cur_ch != 0) error("missing \\0 terminator"); + read(); + } + return l.toArray(); + } + + @SuppressWarnings("unchecked") + private static void writeObject(Object o) throws IOException { + if (o == null) { + write("null"); + } + else if (o instanceof Boolean) { + write(o.toString()); + } + else if (o instanceof Number) { + write(o.toString()); + } + else if (o instanceof String) { + String s = (String)o; + char[] arr = new char[s.length()]; + s.getChars(0, arr.length, arr, 0); + writeObject(arr); + } + else if (o instanceof char[]) { + char[] s = (char[])o; + write('"'); + int l = s.length; + for (int i = 0; i < l; i++) { + char ch = s[i]; + switch (ch) { + case 0: + write("\\u0000"); + break; + case '\r': + write("\\r"); + break; + case '\n': + write("\\n"); + break; + case '\t': + write("\\t"); + break; + case '\b': + write("\\b"); + break; + case '\f': + write("\\f"); + break; + case '"': + case '\\': + write('\\'); + default: + if (tmp_buf_pos >= tmp_buf.length) write(ch); + else tmp_buf[tmp_buf_pos++] = ch; + } + } + write('"'); + } + else if (o instanceof byte[]) { + write('['); + byte[] arr = (byte[])o; + boolean comma = false; + for (int i = 0; i < arr.length; i++) { + if (comma) write(','); + writeUInt(arr[i] & 0xff); + comma = true; + } + write(']'); + } + else if (o instanceof Object[]) { + write('['); + Object[] arr = (Object[])o; + boolean comma = false; + for (int i = 0; i < arr.length; i++) { + if (comma) write(','); + writeObject(arr[i]); + comma = true; + } + write(']'); + } + else if (o instanceof Collection) { + write('['); + boolean comma = false; + for (Iterator<Object> i = ((Collection<Object>)o).iterator(); i.hasNext();) { + if (comma) write(','); + writeObject(i.next()); + comma = true; + } + write(']'); + } + else if (o instanceof Map) { + Map<String,Object> map = (Map<String,Object>)o; + write('{'); + boolean comma = false; + for (Iterator<Map.Entry<String,Object>> i = map.entrySet().iterator(); i.hasNext();) { + if (comma) write(','); + Map.Entry<String,Object> e = i.next(); + writeObject(e.getKey()); + write(':'); + writeObject(e.getValue()); + comma = true; + } + write('}'); + } + else { + throw new IOException("JSON: unsupported object type"); + } + } + + private static byte[] toBytes() throws UnsupportedEncodingException { + int inp_pos = 0; + int out_pos = 0; + while (inp_pos < tmp_buf_pos) { + if (out_pos >= tmp_bbf.length - 4) { + byte[] tmp = new byte[tmp_bbf.length * 2]; + System.arraycopy(tmp_bbf, 0, tmp, 0, out_pos); + tmp_bbf = tmp; + } + int ch = tmp_buf[inp_pos++]; + if (ch < 0x80) { + tmp_bbf[out_pos++] = (byte)ch; + } + else if (ch < 0x800) { + tmp_bbf[out_pos++] = (byte)((ch >> 6) | 0xc0); + tmp_bbf[out_pos++] = (byte)(ch & 0x3f | 0x80); + } + else if (ch < 0x10000) { + tmp_bbf[out_pos++] = (byte)((ch >> 12) | 0xe0); + tmp_bbf[out_pos++] = (byte)((ch >> 6) & 0x3f | 0x80); + tmp_bbf[out_pos++] = (byte)(ch & 0x3f | 0x80); + } + else { + tmp_bbf[out_pos++] = (byte)((ch >> 18) | 0xf0); + tmp_bbf[out_pos++] = (byte)((ch >> 12) & 0x3f | 0x80); + tmp_bbf[out_pos++] = (byte)((ch >> 6) & 0x3f | 0x80); + tmp_bbf[out_pos++] = (byte)(ch & 0x3f | 0x80); + } + } + byte[] res = new byte[out_pos]; + System.arraycopy(tmp_bbf, 0, res, 0, out_pos); + return res; + } + + public static String toJSON(Object o) throws IOException { + assert Protocol.isDispatchThread(); + tmp_buf_pos = 0; + writeObject(o); + return new String(tmp_buf, 0, tmp_buf_pos); + } + + public static byte[] toJASONBytes(Object o) throws IOException { + assert Protocol.isDispatchThread(); + tmp_buf_pos = 0; + writeObject(o); + return toBytes(); + } + + public static byte[] toJSONSequence(Object[] o) throws IOException { + assert Protocol.isDispatchThread(); + if (o == null || o.length == 0) return null; + tmp_buf_pos = 0; + for (int i = 0; i < o.length; i++) { + writeObject(o[i]); + write((char)0); + } + return toBytes(); + } + + public static Object parseOne(String s) throws IOException { + assert Protocol.isDispatchThread(); + reader = new StringReader(s); + err_buf_pos = 0; + err_buf_cnt = 0; + cur_buf_pos = 0; + cur_buf_len = 0; + read(); + return readObject(); + } + + public static Object parseOne(byte[] b) throws IOException { + assert Protocol.isDispatchThread(); + reader = new InputStreamReader(new ByteArrayInputStream(b), "UTF8"); + err_buf_pos = 0; + err_buf_cnt = 0; + cur_buf_pos = 0; + cur_buf_len = 0; + read(); + return readObject(); + } + + public static Object[] parseSequence(byte[] b) throws IOException { + assert Protocol.isDispatchThread(); + reader = new InputStreamReader(new ByteArrayInputStream(b), "UTF8"); + err_buf_pos = 0; + err_buf_cnt = 0; + cur_buf_pos = 0; + cur_buf_len = 0; + read(); + return readSequence(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/Protocol.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/Protocol.java new file mode 100644 index 000000000..7e577ccf5 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/protocol/Protocol.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.protocol; + +import com.windriver.tcf.api.internal.core.LocalPeer; +import com.windriver.tcf.api.internal.core.Transport; +import com.windriver.tcf.api.internal.services.local.LocatorService; +import com.windriver.tcf.api.services.ILocator; + +/** + * + * Class Protocol provides static methods to access Target Communication Framework root objects: + * 1. the framework event queue and dispatch thread; + * 2. local instance of Locator service, which maintains a list of available targets; + * 3. list of open communication channels. + */ +public class Protocol { + + private static IEventQueue event_queue; + + /** + * Before TCF can be used it should be given an object implementing IEventQueue interface. + * The implementation maintains a queue of objects implementing Runnable interface and + * executes <code>run</code> methods of that objects in a sequence by a single thread. + * The thread in referred as TCF event dispatch thread. Objects in the queue are called TCF events. + * Executing <code>run</code> method of an event is also called dispatching of event. + * + * Only few methods in TCF APIs are thread safe - can be invoked from any thread. + * If a method description does not say "can be invoked from any thread" explicitly - + * the method must be invoked from TCF event dispatch thread. All TCF listeners are + * invoked from the dispatch thread. + * + * @param event_queue - IEventQueue implementation. + */ + public static void setEventQueue(IEventQueue event_queue) { + assert Protocol.event_queue == null; + Protocol.event_queue = event_queue; + event_queue.invokeLater(new Runnable() { + + public void run() { + new LocatorService(); + new LocalPeer(); + } + }); + } + + /** + * @return instance of IEventQueue that is used for TCF events. + */ + public static IEventQueue getEventQueue() { + return event_queue; + } + + /** + * Returns true if the calling thread is TCF event dispatch thread. + * Use this call the ensure that a given task is being executed (or not being) + * on dispatch thread. + * + * @return true if running on the dispatch thread. + */ + public static boolean isDispatchThread() { + return event_queue != null && event_queue.isDispatchThread(); + } + + /** + * Causes <code>runnable</code> to have its <code>run</code> + * method called in the dispatch thread of the framework. + * Events are dispatched in same order as queued. + * If invokeLater is called from the dispatching thread + * the <i>runnable.run()</i> will still be deferred until + * all pending events have been processed. + * + * This method can be invoked from any thread. + * + * @param runnable the <code>Runnable</code> whose <code>run</code> + * method should be executed asynchronously. + */ + public static void invokeLater(Runnable runnable) { + event_queue.invokeLater(runnable); + } + + /** + * Causes <code>runnable</code> to have its <code>run</code> + * method called in the dispatch thread of the framework. + * Calling thread is suspended until the method is executed. + * + * This method can be invoked from any thread. + * + * @param runnable the <code>Runnable</code> whose <code>run</code> + * method should be executed on dispatch thread. + */ + public static void invokeAndWait(final Runnable runnable) { + if (event_queue.isDispatchThread()) { + runnable.run(); + } + else { + Runnable r = new Runnable() { + public void run() { + try { + runnable.run(); + } + finally { + synchronized (this) { + notify(); + } + } + } + }; + synchronized (r) { + event_queue.invokeLater(r); + try { + r.wait(); + } + catch (InterruptedException x) { + throw new Error(x); + } + } + } + } + + /** + * Get instance of the framework locator service. + * The service can be used to discover available remote peers. + * + * @return instance of ILocator. + */ + public static ILocator getLocator() { + return LocatorService.getLocator(); + } + + /** + * Return an array of all open channels. + * @return an array of IChannel + */ + public static IChannel[] getOpenChannels() { + return Transport.getOpenChannels(); + } + + /** + * Interface to be implemented by clients willing to be notified when + * new TCF communication channel is opened. + */ + public interface ChannelOpenListener { + public void onChannelOpen(IChannel channel); + } + + /** + * Add a listener that will be notified when new channel is opened. + * @param listener + */ + public static void addChannelOpenListener(ChannelOpenListener listener) { + Transport.addChanalOpenListener(listener); + } + + /** + * Remove channel opening listener. + * @param listener + */ + public static void removeChannelOpenListener(ChannelOpenListener listener) { + Transport.removeChanalOpenListener(listener); + } + + /** + * Transmit TCF event message. + * The message is sent to all open communication channels – broadcasted. + */ + public static void sendEvent(String service_name, String event_name, byte[] data) { + Transport.sendEvent(service_name, event_name, data); + } + + /** + * Call back after TCF messages sent by this host up to this moment are delivered + * to their intended target. This method is intended for synchronization of messages + * across multiple channels. + * + * Note: Cross channel synchronization can reduce performance and throughput. + * Most clients don't need cross channel synchronization and should not call this method. + * + * @param done will be executed by dispatch thread after communication + * messages are delivered to corresponding targets. + */ + public static void sync(Runnable done) { + Transport.sync(done); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IBreakpoints.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IBreakpoints.java new file mode 100644 index 000000000..fbcb5e0b5 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IBreakpoints.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * Breakpoint is represented by unique identifier and set of properties. + * Breakpoint identifier (String id) needs to be unique across all hosts and targets. + * + * Breakpoint properties (Map<String,Object>) is extendable collection of named attributes, + * which define breakpoint location and behavior. This module defines some common + * attribute names (see PROP_*), host tools and target agents may support additional attributes. + * + * For each breakpoint a target agent maintains another extendable collection of named attributes: + * breakpoint status (Map<String,Object>, see STATUS_*). While breakpoint properties are + * persistent and represent user input, breakpoint status reflects dynamic target agent reports + * about breakpoint current state, like actual addresses where breakpoint is planted or planting errors. + */ +public interface IBreakpoints extends IService { + + /** + * Service name. + */ + static final String NAME = "Breakpoints"; + + /** + * Breakpoint property names. + */ + static final String + PROP_ID = "ID", // String + PROP_ENABLED = "Enabled", // Boolean + PROP_ADDRESS = "Address", // String + PROP_CONDITION = "Condition", // String + PROP_FILE = "File", // String + PROP_LINE = "Line", // Number + PROP_COLUMN = "Column"; // Number + + /** + * Breakpoint status field names. + */ + static final String + STATUS_PLANTED = "Planted", // Array of addresses + STATUS_ERROR = "Error", // String + STATUS_FILE = "File", // String + STATUS_LINE = "Line", // Number + STATUS_COLUMN = "Column"; // Number + + /** + * Call back interface for breakpoint service commands. + */ + interface DoneCommand { + void doneCommand(IToken token, Exception error); + } + + /** + * Download breakpoints data to target agent. + * The command is intended to be used only to initialize target breakpoints table + * when communication channel is open. After that, host should + * notify target about (incremental) changes in breakpoint data by sending + * add, change and remove commands. + * + * @param properties - array of breakpoints. + * @param done - command result call back object. + */ + IToken set(Map<String,Object>[] properties, DoneCommand done); + + /** + * Called when breakpoint is added into breakpoints table. + * @param properties - breakpoint properties. + * @param done - command result call back object. + */ + IToken add(Map<String,Object> properties, DoneCommand done); + + /** + * Called when breakpoint properties are changed. + * @param properties - breakpoint properties. + * @param done - command result call back object. + */ + IToken change(Map<String,Object> properties, DoneCommand done); + + /** + * Tell target to change (only) PROP_ENABLED breakpoint property 'true'. + * @param ids - array of enabled breakpoint identifiers. + * @param done - command result call back object. + */ + IToken enable(String[] ids, DoneCommand done); + + /** + * Tell target to change (only) PROP_ENABLED breakpoint property to 'false'. + * @param ids - array of disabled breakpoint identifiers. + * @param done - command result call back object. + */ + IToken disable(String[] ids, DoneCommand done); + + /** + * Tell target to remove breakpoint. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */ + IToken remove(String[] ids, DoneCommand done); + + /** + * Upload IDs of breakpoints known to target agent. + * @param done - command result call back object. + */ + IToken getIDs(DoneGetIDs done); + + interface DoneGetIDs { + void doneGetIDs(IToken token, Exception error, String[] ids); + } + + /** + * Upload properties of given breakpoint from target agent breakpoint table. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */ + IToken getProperties(String id, DoneGetProperties done); + + interface DoneGetProperties { + void doneGetProperties(IToken token, Exception error, Map<String,Object> properties); + } + + /** + * Upload status of given breakpoint from target agent. + * @param id - unique breakpoint identifier. + * @param done - command result call back object. + */ + IToken getStatus(String id, DoneGetStatus done); + + interface DoneGetStatus { + void doneGetStatus(IToken token, Exception error, Map<String,Object> status); + } + + /** + * Breakpoints service events listener. + */ + interface BreakpointsListener { + + /** + * Called when breakpoint status changes. + * @param id - unique breakpoint identifier. + * @param status - breakpoint status. + */ + void breakpointStatusChanged(String id, Map<String,Object> status); + } + + void addListener(BreakpointsListener listener); + + void removeListener(BreakpointsListener listener); +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IDiagnostics.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IDiagnostics.java new file mode 100644 index 000000000..9552c4a6d --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IDiagnostics.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * This is optional service that can be implemented by a peer. + * If implemented, the service can be used for testing of the peer and + * communication channel functionality and reliability. + */ + +public interface IDiagnostics extends IService { + + public static final String NAME = "Diagnostics"; + + public IToken echo(String s, DoneEcho done); + + public interface DoneEcho { + public void doneEcho(IToken token, Throwable error, String s); + } + + public IToken getTestList(DoneGetTestList done); + + public interface DoneGetTestList { + public void doneGetTestList(IToken token, Throwable error, String[] list); + } + + public IToken runTest(String s, DoneRunTest done); + + public interface DoneRunTest { + public void doneRunTest(IToken token, Throwable error, String context_id); + } + + public IToken cancelTest(String context_id, DoneCancelTest done); + + public interface DoneCancelTest { + public void doneCancelTest(IToken token, Throwable error); + } + + public IToken getSymbol(String context_id, String symbol_name, DoneGetSymbol done); + + public interface DoneGetSymbol { + public void doneGetSymbol(IToken token, Throwable error, ISymbol symbol); + } + + public interface ISymbol { + public String getSectionName(); + public Number getValue(); + public boolean isUndef(); + public boolean isCommon(); + public boolean isGlobal(); + public boolean isLocal(); + public boolean isAbs(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IFileSystem.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IFileSystem.java new file mode 100644 index 000000000..70213469b --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IFileSystem.java @@ -0,0 +1,696 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.io.IOException; +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * File System service provides file transfer (and more generally file + * system access) functionality in TCF. The service design is + * derived from SSH File Transfer Protocol specifications. + * + * Request Synchronization and Reordering + * + * The protocol and implementations MUST process requests relating to + * the same file in the order in which they are received. In other + * words, if an application submits multiple requests to the server, the + * results in the responses will be the same as if it had sent the + * requests one at a time and waited for the response in each case. For + * example, the server may process non-overlapping read/write requests + * to the same file in parallel, but overlapping reads and writes cannot + * be reordered or parallelized. However, there are no ordering + * restrictions on the server for processing requests from two different + * file transfer connections. The server may interleave and parallelize + * them at will. + * + * There are no restrictions on the order in which responses to + * outstanding requests are delivered to the client, except that the + * server must ensure fairness in the sense that processing of no + * request will be indefinitely delayed even if the client is sending + * other requests so that there are multiple outstanding requests all + * the time. + * + * There is no limit on the number of outstanding (non-acknowledged) + * requests that the client may send to the server. In practice this is + * limited by the buffering available on the data stream and the queuing + * performed by the server. If the server's queues are full, it should + * not read any more data from the stream, and flow control will prevent + * the client from sending more requests. + * + * File Names + * + * This protocol represents file names as strings. File names are + * assumed to use the slash ('/') character as a directory separator. + * + * File names starting with a slash are "absolute", and are relative to + * the root of the file system. Names starting with any other character + * are relative to the user's default directory (home directory). Client + * can use 'user()' command to retrieve current user home directory. + * + * Servers SHOULD interpret a path name component ".." as referring to + * the parent directory, and "." as referring to the current directory. + * If the server implementation limits access to certain parts of the + * file system, it must be extra careful in parsing file names when + * enforcing such restrictions. There have been numerous reported + * security bugs where a ".." in a path name has allowed access outside + * the intended area. + * + * An empty path name is valid, and it refers to the user's default + * directory (usually the user's home directory). + * + * Otherwise, no syntax is defined for file names by this specification. + * Clients should not make any other assumptions; however, they can + * splice path name components returned by readdir() together + * using a slash ('/') as the separator, and that will work as expected. + */ +public interface IFileSystem extends IService { + + /** + * Service name. + */ + static final String NAME = "FileSystem"; + + /** + * Flags to be used with open() method. + */ + static final int + + /** + * Open the file for reading. + */ + O_READ = 0x00000001, + + /** + * Open the file for writing. If both this and O_READ are + * specified, the file is opened for both reading and writing. + */ + O_WRITE = 0x00000002, + + /** + * Force all writes to append data at the end of the file. + */ + O_APPEND = 0x00000004, + + /** + * If this flag is specified, then a new file will be created if one + * does not already exist (if O_TRUNC is specified, the new file will + * be truncated to zero length if it previously exists). + */ + O_CREAT = 0x00000008, + + /** + * Forces an existing file with the same name to be truncated to zero + * length when creating a file by specifying O_CREAT. + * O_CREAT MUST also be specified if this flag is used. + */ + O_TRUNC = 0x00000010, + + /** + * Causes the request to fail if the named file already exists. + * O_CREAT MUST also be specified if this flag is used. + */ + O_EXCL = 0x00000020; + + /** + * Flags to be used together with FileAttrs. + * The flags specify which of the fields are present. Those fields + * for which the corresponding flag is not set are not present (not + * included in the message). + */ + static final int + ATTR_SIZE = 0x00000001, + ATTR_UIDGID = 0x00000002, + ATTR_PERMISSIONS = 0x00000004, + ATTR_ACMODTIME = 0x00000008; + + /** + * FileAttrs is used both when returning file attributes from + * the server and when sending file attributes to the server. When + * sending it to the server, the flags field specifies which attributes + * are included, and the server will use default values for the + * remaining attributes (or will not modify the values of remaining + * attributes). When receiving attributes from the server, the flags + * specify which attributes are included in the returned data. The + * server normally returns all attributes it knows about. + */ + final static class FileAttrs { + + /** + * The `flags' specify which of the fields are present. + */ + public final int flags; + + /** + * The `size' field specifies the size of the file in bytes. + */ + public final long size; + + /** + * The `uid' and `gid' fields contain numeric Unix-like user and group + * identifiers, respectively. + */ + public final int uid; + public final int gid; + + /** + * The `permissions' field contains a bit mask of file permissions as + * defined by posix [1]. + */ + public final int permissions; + + /** + * The `atime' and `mtime' contain the access and modification times of + * the files, respectively. They are represented as milliseconds from + * midnight Jan 1, 1970 in UTC. + */ + public final long atime; + public final long mtime; + + /** + * Additional (non-standard) attributes. + */ + public final Map<String,Object> attributes; + + public FileAttrs(int flags, long size, int uid, int gid, + int permissions, long atime, long mtime, Map<String,Object> attributes) { + this.flags = flags; + this.size = size; + this.uid = uid; + this.gid = gid; + this.permissions = permissions; + this.atime = atime; + this.mtime = mtime; + this.attributes = attributes; + } + + /** + * Determines if the file system object is a file on the remote file system. + * + * @return true if and only if the object on the remote system can be considered to have "contents" that + * have the potential to be read and written as a byte stream. + */ + public boolean isFile() { + if ((flags & ATTR_PERMISSIONS) == 0) return false; + return (permissions & S_IFMT) == S_IFREG; + } + + /** + * Determines if the file system object is a directory on the remote file system. + * + * @return true if and only if the object on the remote system is a directory. + * That is, it contains entries that can be interpreted as other files. + */ + public boolean isDirectory() { + if ((flags & ATTR_PERMISSIONS) == 0) return false; + return (permissions & S_IFMT) == S_IFDIR; + } + } + + /** + * The following flags are defined for the 'permissions' field: + */ + static final int + S_IFMT = 0170000, // bitmask for the file type bitfields + S_IFSOCK = 0140000, // socket + S_IFLNK = 0120000, // symbolic link + S_IFREG = 0100000, // regular file + S_IFBLK = 0060000, // block device + S_IFDIR = 0040000, // directory + S_IFCHR = 0020000, // character device + S_IFIFO = 0010000, // fifo + S_ISUID = 0004000, // set UID bit + S_ISGID = 0002000, // set GID bit (see below) + S_ISVTX = 0001000, // sticky bit (see below) + S_IRWXU = 00700, // mask for file owner permissions + S_IRUSR = 00400, // owner has read permission + S_IWUSR = 00200, // owner has write permission + S_IXUSR = 00100, // owner has execute permission + S_IRWXG = 00070, // mask for group permissions + S_IRGRP = 00040, // group has read permission + S_IWGRP = 00020, // group has write permission + S_IXGRP = 00010, // group has execute permission + S_IRWXO = 00007, // mask for permissions for others (not in group) + S_IROTH = 00004, // others have read permission + S_IWOTH = 00002, // others have write permisson + S_IXOTH = 00001; // others have execute permission + + final static class DirEntry { + /** + * `filename' is a file name being returned. It is a relative name within + * the directory, without any path components; + */ + public final String filename; + + /** + * `longname' is an expanded format for the file name, similar to what + * is returned by "ls -l" on Unix systems. + * The format of the `longname' field is unspecified by this protocol. + * It MUST be suitable for use in the output of a directory listing + * command (in fact, the recommended operation for a directory listing + * command is to simply display this data). However, clients SHOULD NOT + * attempt to parse the longname field for file attributes; they SHOULD + * use the attrs field instead. + */ + public final String longname; + + /** + * `attrs' is the attributes of the file. + */ + public final FileAttrs attrs; + + public DirEntry(String filename, String longname, FileAttrs attrs) { + this.filename = filename; + this.longname = longname; + this.attrs = attrs; + } + } + + /** + * Opaque representation of open file handle. + * Note: open file handle can be used only with service instance that + * created the handle. + */ + interface IFileHandle { + IFileSystem getService(); + } + + /** + * Status codes. + */ + static final int + + /** + * Indicates successful completion of the operation. + */ + STATUS_OK = 0, + + /** + * Indicates end-of-file condition; for read() it means that no + * more data is available in the file, and for readdir() it + * indicates that no more files are contained in the directory. + */ + STATUS_EOF = 1, + + /** + * This code is returned when a reference is made to a file which + * should exist but doesn't. + */ + STATUS_NO_SUCH_FILE = 2, + + /** + * is returned when the authenticated user does not have sufficient + * permissions to perform the operation. + */ + STATUS_PERMISSION_DENIED = 3, + + /** + * is a generic catch-all error message; it should be returned if an + * error occurs for which there is no more specific error code. + * + */ + STATUS_FAILURE = 4, + + /** + * may be returned if a badly formatted packet or protocol + * incompatibility is detected. + */ + STATUS_BAD_MESSAGE = 5, + + /** + * is a pseudo-error which indicates that the client has no + * connection to the server (it can only be generated locally by the + * client, and MUST NOT be returned by servers). + */ + STATUS_NO_CONNECTION = 6, + + /** + * is a pseudo-error which indicates that the connection to the + * server has been lost (it can only be generated locally by the + * client, and MUST NOT be returned by servers). + */ + STATUS_CONNECTION_LOST = 7, + + /** + * indicates that an attempt was made to perform an operation which + * is not supported for the server (it may be generated locally by + * the client if e.g. the version number exchange indicates that a + * required feature is not supported by the server, or it may be + * returned by the server if the server does not implement an + * operation). + */ + STATUS_OP_UNSUPPORTED = 8; + + abstract static class FileSystemException extends IOException { + + protected FileSystemException(String message) { + super(message); + } + + protected FileSystemException(Exception x) { + super(x.getMessage()); + initCause(x); + } + + public abstract int getStatus(); + } + + /** + * Open or create a file on a remote system. + * + * @param file_name specifies the file name. See 'File Names' for more information. + * @param flags is a bit mask of O_* flags. + * @param attrs specifies the initial attributes for the file. + * Default values will be used for those attributes that are not specified. + * @param done is call back object. + * @return pending command handle. + */ + IToken open(String file_name, int flags, FileAttrs attrs, DoneOpen done); + + interface DoneOpen { + void doneOpen(IToken token, FileSystemException error, IFileHandle handle); + } + + /** + * Close a file on a remote system. + * + * @param handle is a handle previously returned in the response to + * open() or opendir(). + * @param done is call back object. + * @return pending command handle. + */ + IToken close(IFileHandle handle, DoneClose done); + + interface DoneClose { + void doneClose(IToken token, FileSystemException error); + } + + /** + * Read bytes from an open file. + * In response to this request, the server will read as many bytes as it + * can from the file (up to `len'), and return them in a byte array. + * If an error occurs or EOF is encountered, the server may return + * fewer bytes then requested. Call back method doneRead() argument 'error' + * will be not null in case of error, and argument 'eof' will be + * true in case of EOF. For normal disk files, it is guaranteed + * that this will read the specified number of bytes, or up to end of file + * or error. For e.g. device files this may return fewer bytes than requested. + * + * @param handle is an open file handle returned by open(). + * @param offset is the offset (in bytes) relative + * to the beginning of the file from where to start reading. + * @param len is the maximum number of bytes to read. + * @param done is call back object. + * @return pending command handle. + */ + IToken read(IFileHandle handle, long offset, int len, DoneRead done); + + interface DoneRead { + void doneRead(IToken token, FileSystemException error, byte[] data, boolean eof); + } + + /** + * Write bytes into an open file. + * The write will extend the file if writing beyond the end of the file. + * It is legal to write way beyond the end of the file; the semantics + * are to write zeroes from the end of the file to the specified offset + * and then the data. + * + * @param handle is an open file handle returned by open(). + * @param offset is the offset (in bytes) relative + * to the beginning of the file from where to start writing. + * @param data is byte array that contains data for writing. + * @param data_pos if offset in 'data' of first byte to write. + * @param data_size is the number of bytes to write. + * @param done is call back object. + * @return pending command handle. + */ + IToken write(IFileHandle handle, long offset, + byte[] data, int data_pos, int data_size, DoneWrite done); + + interface DoneWrite { + void doneWrite(IToken token, FileSystemException error); + } + + /** + * Retrieve file attributes. + * + * @param path - specifies the file system object for which + * status is to be returned. + * @param done is call back object. + * @return pending command handle. + */ + IToken stat(String path, DoneStat done); + + /** + * Retrieve file attributes. + * Unlike 'stat()', 'lstat()' does not follow symbolic links. + * + * @param path - specifies the file system object for which + * status is to be returned. + * @param done is call back object. + * @return pending command handle. + */ + IToken lstat(String path, DoneStat done); + + /** + * Retrieve file attributes for an open file (identified by the file handle). + * + * @param handle is a file handle returned by 'open()'. + * @param done is call back object. + * @return pending command handle. + */ + IToken fstat(IFileHandle handle, DoneStat done); + + interface DoneStat { + void doneStat(IToken token, FileSystemException error, FileAttrs attrs); + } + + /** + * Set file attributes. + * This request is used for operations such as changing the ownership, + * permissions or access times, as well as for truncating a file. + * An error will be returned if the specified file system object does + * not exist or the user does not have sufficient rights to modify the + * specified attributes. + * + * @param path specifies the file system object (e.g. file or directory) + * whose attributes are to be modified. + * @param attrs specifies the modifications to be made to file attributes. + * @param done is call back object. + * @return pending command handle. + */ + IToken setstat(String path, FileAttrs attrs, DoneSetStat done); + + /** + * Set file attributes for an open file (identified by the file handle). + * This request is used for operations such as changing the ownership, + * permissions or access times, as well as for truncating a file. + * + * @param handle is a file handle returned by 'open()'. + * @param attrs specifies the modifications to be made to file attributes. + * @param done is call back object. + * @return pending command handle. + */ + IToken fsetstat(IFileHandle handle, FileAttrs attrs, DoneSetStat done); + + interface DoneSetStat { + void doneSetStat(IToken token, FileSystemException error); + } + + /** + * The opendir() command opens a directory for reading. + * Once the directory has been successfully opened, files (and + * directories) contained in it can be listed using readdir() requests. + * When the client no longer wishes to read more names from the + * directory, it SHOULD call close() for the handle. The handle + * should be closed regardless of whether an error has occurred or not. + + * @param path - name of the directory to be listed (without any trailing slash). + * @param done - result call back object. + * @return pending command handle. + */ + IToken opendir(String path, DoneOpen done); + + /** + * The files in a directory can be listed using the opendir() and + * readdir() requests. Each readdir() request returns one + * or more file names with full file attributes for each file. The + * client should call readdir() repeatedly until it has found the + * file it is looking for or until the server responds with a + * message indicating an error or end of file. The client should then + * close the handle using the close() request. + * Note: directory entries "." and ".." are NOT included into readdir() + * response. + * @param handle - file handle created by opendir() + * @param done - result call back object. + * @return pending command handle. + */ + IToken readdir(IFileHandle handle, DoneReadDir done); + + interface DoneReadDir { + void doneReadDir(IToken token, FileSystemException error, DirEntry[] entries, boolean eof); + } + + /** + * Create a directory on the server. + * + * @param path - specifies the directory to be created. + * @param attrs - new directory attributes. + * @param done - result call back object. + * @return pending command handle. + */ + IToken mkdir(String path, FileAttrs attrs, DoneMkDir done); + + interface DoneMkDir { + void doneMkDir(IToken token, FileSystemException error); + } + + /** + * Remove a directory. + * An error will be returned if no directory + * with the specified path exists, or if the specified directory is not + * empty, or if the path specified a file system object other than a + * directory. + * + * @param path - specifies the directory to be removed. + * @param done - result call back object. + * @return pending command handle. + */ + IToken rmdir(String path, DoneRemove done); + + interface DoneRemove { + void doneRemove(IToken token, FileSystemException error); + } + + /** + * Retrieve file system roots - top level file system objects. + * UNIX file system can report just one root with path "/". Other types of systems + * can have more the one root. For example, Windows server can return multiple roots: + * one per disc (e.g. "/C:/", "/D:/", etc.). Note: even Windows implementation of + * the service must use forward slash as directory separator, and must start + * absolute path with "/". Server should implement proper translation of + * protocol file names to OS native names and back. + * + * @param done - result call back object. + * @return pending command handle. + */ + IToken roots(DoneRoots done); + + interface DoneRoots { + void doneRoots(IToken token, FileSystemException error, DirEntry[] entries); + } + + /** + * Remove a file or symbolic link. + * This request cannot be used to remove directories. + * + * @param file_name is the name of the file to be removed. + * @param done - result call back object. + * @return pending command handle. + */ + IToken remove(String file_name, DoneRemove done); + + /** + * Canonicalize any given path name to an absolute path. + * This is useful for converting path names containing ".." components or + * relative pathnames without a leading slash into absolute paths. + * + * @param path specifies the path name to be canonicalized. + * @param done - result call back object. + * @return pending command handle. + */ + IToken realpath(String path, DoneRealPath done); + + interface DoneRealPath { + void doneRealPath(IToken token, FileSystemException error, String path); + } + + /** + * Rename a file. + * It is an error if there already exists a file + * with the name specified by 'new_path'. The server may also fail rename + * requests in other situations, for example if `old_path' and `new_path' + * point to different file systems on the server. + * + * @param old_path is the name of an existing file or directory. + * @param new_path is the new name for the file or directory. + * @param done - result call back object. + * @return pending command handle. + */ + IToken rename(String old_path, String new_path, DoneRename done); + + interface DoneRename { + void doneRename(IToken token, FileSystemException error); + } + + /** + * Read the target of a symbolic link. + * + * @param path specifies the path name of the symbolic link to be read. + * @param done - result call back object. + * @return pending command handle. + */ + IToken readlink(String path, DoneReadLink done); + + interface DoneReadLink { + void doneReadLink(IToken token, FileSystemException error, String path); + } + + /** + * Create a symbolic link on the server. + * + * @param link_path specifies the path name of the symbolic link to be created. + * @param target_path specifies the target of the symbolic link. + * @param done - result call back object. + * @return pending command handle. + */ + IToken symlink(String link_path, String target_path, DoneSymLink done); + + interface DoneSymLink { + void doneSymLink(IToken token, FileSystemException error); + } + + /** + * Copy a file on remote system. + * + * @param src_path specifies the path name of the file to be copied. + * @param dst_path specifies destination file name. + * @param copy_permissions - if true then copy source file permissions. + * @param copy_ownership - if true then copy source file UID and GID. + * @param done - result call back object. + * @return pending command handle. + */ + IToken copy(String src_path, String dst_path, + boolean copy_permissions, boolean copy_ownership, DoneCopy done); + + interface DoneCopy { + void doneCopy(IToken token, FileSystemException error); + } + + /** + * Retrieve information about user account, which is used by server + * to access file system on behalf of the client. + * + * @param done - result call back object. + * @return pending command handle. + */ + IToken user(DoneUser done); + + interface DoneUser { + void doneUser(IToken token, FileSystemException error, + int real_uid, int effective_uid, int real_gid, int effective_gid, + String home); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILineNumbers.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILineNumbers.java new file mode 100644 index 000000000..2547e6f1e --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILineNumbers.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * Line numbers service associates locations in the source files with the corresponding + * machine instruction addresses in the executable object. + */ +public interface ILineNumbers extends IService { + + static final String NAME = "LineNumbers"; + + /** + * TextArea represent a continues area in source text mapped to + * continues range of code addresses. + * Line and columns are counted starting from 0. + * File name can be relative path, in such case client should + * use TextArea directory name as origin for the path. + * File and directory names are valid on a host where code was compiled. + * It is client responsibility to map names to this host file system. + */ + final class CodeArea { + public final String directory; + public final String file; + public final int start_line; + public final int start_column; + public final int end_line; + public final int end_column; + public final Number start_address; + public final Number end_address; + public final int isa; + public final boolean is_statement; + public final boolean basic_block; + public final boolean prologue_end; + public final boolean epilogue_begin; + + public CodeArea(String directory, String file, int start_line, int start_column, + int end_line, int end_column, Number start_address, Number end_address, int isa, + boolean is_statement, boolean basic_block, + boolean prologue_end, boolean epilogue_begin) { + this.directory = directory; + this.file = file; + this.start_line = start_line; + this.start_column = start_column; + this.end_line = end_line; + this.end_column = end_column; + this.start_address = start_address; + this.end_address = end_address; + this.isa = isa; + this.is_statement = is_statement; + this.basic_block = basic_block; + this.prologue_end = prologue_end; + this.epilogue_begin = epilogue_begin; + } + } + + IToken mapToSource(String context_id, Number start_address, Number end_address, DoneMapToSource done); + + interface DoneMapToSource { + void doneMapToSource(IToken token, Exception error, CodeArea[] areas); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILocator.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILocator.java new file mode 100644 index 000000000..9116d9d7f --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ILocator.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * ILocator service uses transport layer to search for peers and to collect data about + * peer’s attributes and capabilities (services). Discovery mechanism depends on transport protocol + * and is part of that protocol handler. Targets, known to other hosts, can be found through + * remote instances of ILocator service. Automatically discovered targets require no further + * configuration. Additional targets can be configured manually. + * + * Clients should use Protocol.getLocator() to obtain local instance of ILocator, + * then ILocator.getPeers() can be used to get list of available peers (hosts and targets). + */ + +public interface ILocator extends IService { + + static final String NAME = "Locator"; + + /** + * Auto-configuration command and response codes. + */ + static final int + CONF_REQ_INFO = 1, + CONF_PEER_INFO = 2; + + /** + * @return Locator service name: "Locator" + */ + String getName(); + + /** + * Get map (ID -> IPeer) of available peers (hosts and targets). + * The method return cached (currently known to the framework) list of peers. + * The list is updated according to event received from transport layer + */ + Map<String,IPeer> getPeers(); + + /** + * Redirect this service channel to given peer using this service as a proxy. + * @param peer_id - Peer ID. + */ + IToken redirect(String peer_id, DoneRedirect done); + + interface DoneRedirect { + void doneRedirect(IToken token, Exception error); + } + + /** + * Call back after TCF messages sent to this target up to this moment are delivered. + * This method is intended for synchronization of messages + * across multiple channels. + * + * Note: Cross channel synchronization can reduce performance and throughput. + * Most clients don't need channel synchronization and should not call this method. + * + * @param done will be executed by dispatch thread after communication + * messages are delivered to corresponding targets. + * + * This is internal API, TCF clients should use {@code com.windriver.tcf.api.protocol.Protocol}. + */ + IToken sync(DoneSync done); + + interface DoneSync { + void doneSync(IToken token); + } + + /** + * Add a listener for ILocator service events. + */ + void addListener(LocatorListener listener); + + /** + * Remove a listener for ILocator service events. + */ + void removeListener(LocatorListener listener); + + interface LocatorListener { + void peerAdded(IPeer peer); + + void peerChanged(IPeer peer); + + void peerRemoved(String id); + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IMemory.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IMemory.java new file mode 100644 index 000000000..0f01c4a95 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IMemory.java @@ -0,0 +1,247 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * IMemory service provides basic operations to read/write memory on a target. + */ +public interface IMemory extends IService { + + static final String NAME = "Memory"; + + /** + * Context property names. + */ + static final String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_PROCESS_ID = "ProcessID", + PROP_BIG_ENDIAN = "BigEndian", + PROP_ADDRESS_SIZE = "AddressSize"; + + /** + * Retrieve context info for given context ID. + * + * @param id – context ID. + * @param done - call back interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client call back interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, MemoryContext context); + } + + /** + * Retrieve contexts available for memory commands. + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getChildren commands. + * @param done - call back interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client call back interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * Memory access mode: + * Carry on when some of the memory cannot be accessed and + * return MemoryError at the end if any of the bytes + * were not processed correctly. + */ + final static int MODE_CONTINUEONERROR = 0x1; + + /** + * Memory access mode: + * Verify result of memory operations (by reading and comparing). + */ + final static int MODE_VERIFY = 0x2; + + interface MemoryContext { + + /** + * Retrieve context ID. + * Same as (String)getProperties().get(“ID”) + */ + String getID(); + + /** + * Retrieve parent context ID. + * Same as (String)getProperties().get(“ParentID”) + */ + String getParentID(); + + /** + * Retrieves process ID, if applicable. + * @return process system ID. + */ + int getProcessID(); + + /** + * Retrieve memory endianess. + * @return true if memory id big-endian. + */ + boolean isBigEndian(); + + /** + * Retrieve memory address size. + * @return number of bytes used to store memory address value. + */ + int getAddressSize(); + + /** + * Retrieve context properties. + */ + Map<String,Object> getProperties(); + + /** + * Set target memory. + * If 'word_size' is 0 it means client does not care about word size. + */ + IToken set(Number addr, int word_size, byte[] buf, + int offs, int size, int mode, DoneMemory done); + + /** + * Read target memory. + */ + IToken get(Number addr, int word_size, byte[] buf, + int offs, int size, int mode, DoneMemory done); + + /** + * Fill target memory with given pattern. + * 'size' is number of bytes to fill. + */ + IToken fill(Number addr, int word_size, byte[] value, + int size, int mode, DoneMemory done); + } + + /** + * Client call back interface for set(), get() and fill() commands. + */ + interface DoneMemory { + public void doneMemory(IToken token, MemoryError error); + } + + class MemoryError extends Exception { + + private static final long serialVersionUID = 1L; + + public MemoryError(String msg) { + super(msg); + } + } + + /** + * ErrorOffset interface can be implemented by MemoryError object, + * which is returned by get, set and fill commands. + * + * get/set/fill () returns this exception when reading failed + * for some but not all bytes, and MODE_CONTINUEONERROR + * has been set in mode. (For example, when only part of the request + * translates to valid memory addresses.) + * Exception.getMessage can be used for generalized message of the + * possible reasons of partial memory operation. + */ + interface ErrorOffset { + + // Error may have per byte information + final static int + BYTE_VALID = 0x00, + BYTE_UNKNOWN = 0x01, // e.g. out of range + BYTE_INVALID = 0x02, + BYTE_CANNOT_READ = 0x04, + BYTE_CANNOT_WRITE = 0x08; + + int getStatus(int offset); + + /** + * Returns the detail message string about the + * byte associated with specified location. + * @return the detail error message string. + */ + String getMessage(int offset); + + } + + /** + * Add memory service event listener. + * @param listener - event listener implementation. + */ + void addListener(MemoryListener listener); + + /** + * Remove memory service event listener. + * @param listener - event listener implementation. + */ + void removeListener(MemoryListener listener); + + /** + * Memory event listener is notified when memory context hierarchy + * changes, and when memory is modified by memory service commands. + */ + interface MemoryListener { + + /** + * Called when a new memory access context(s) is created. + */ + void contextAdded(MemoryContext[] contexts); + + /** + * Called when a memory access context(s) properties changed. + */ + void contextChanged(MemoryContext[] contexts); + + /** + * Called when memory access context(s) is removed. + */ + void contextRemoved(String[] context_ids); + + /** + * Called when target memory content was changed and clients + * need to update themselves. Clients, at least, should invalidate + * corresponding cached memory data. + * Not every change is notified - it is not possible, + * only those, which are not caused by normal execution of the debuggee. + * ‘addr’ and ‘size’ can be null if unknown. + */ + void memoryChanged(String context_id, Number[] addr, long[] size); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IProcesses.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IProcesses.java new file mode 100644 index 000000000..531e8e57a --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IProcesses.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * IProcesses service provides access to the target OS's process + * information, allows to start and terminate a process, and allows + * to attach and detach a process for debugging. Debug services, + * like IMemory and IRunControl, require a process to be attached + * before they can access it. + */ +public interface IProcesses extends IService { + + static final String NAME = "Processes"; + + /** + * Retrieve context info for given context ID. + * A context corresponds to an execution thread, process, address space, etc. + * Context IDs are valid across TCF services, so it is allowed to issue + * 'IProcesses.getContext' command with a context that was obtained, + * for example, from Memory service. + * However, 'Processes.getContext' is supposed to return only process specific data, + * If the ID is not a process ID, 'IProcesses.getContext' may not return any + * useful information + * + * @param id – context ID. + * @param done - call back interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client call back interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, ProcessContext context); + } + + /** + * Retrieve children of given context. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * @param done - call back interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, boolean attached_only, DoneGetChildren done); + + /** + * Client call back interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * Context property names. + */ + static final String + /** The TCF context ID */ + PROP_ID = "ID", + + /** The TCF parent context ID */ + PROP_PARENTID = "ParentID", + + /** Is the context attached */ + PROP_ATTACHED = "Attached", + + /** Can terminate the context */ + PROP_CAN_TERMINATE = "CanTerminate", + + /** Process name. Client UI can show this name to a user */ + PROP_NAME = "Name"; + + interface ProcessContext { + + /** + * Get context ID. + * Same as getProperties().get(“ID”) + */ + String getID(); + + /** + * Get parent context ID. + * Same as getProperties().get(“ParentID”) + */ + String getParentID(); + + /** + * Get process name. + * Client UI can show this name to a user. + * Same as getProperties().get(“Name”) + */ + String getName(); + + /** + * Utility method to read context property PROP_ATTACHED. + * Services like IRunControl, IMemory, IBreakpoints work only with attached processes. + * @return value of PROP_ATTACHED. + */ + boolean isAttached(); + + /** + * Utility method to read context property PROP_CAN_TERMINATE. + * @return value of PROP_CAN_TERMINATE. + */ + boolean canTerminate(); + + /** + * Get all available context properties. + * @return Map 'property name' -> 'property value' + */ + Map<String, Object> getProperties(); + + /** + * Attach debugger to a process. + * Services like IRunControl, IMemory, IBreakpoints work only with attached processes. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken attach(DoneCommand done); + + /** + * Detach debugger from a process. + * Process execution will continue without debugger supervision. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken detach(DoneCommand done); + + /** + * Terminate a process. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken terminate(DoneCommand done); + + /** + * Send a signal to a process. + * @param signal - signal ID. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken signal(int signal, DoneCommand done); + } + + interface DoneCommand { + void doneCommand(IToken token, Exception error); + } + + /** + * Get default set of environment variables used to start a new process. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken getEnvironment(DoneGetEnvironment done); + + interface DoneGetEnvironment { + void doneGetEnvironment(IToken token, Exception error, Map<String,String> environment); + } + + /** + * Start a new process on remote machine. + * @param directory - initial value of working directory for the process. + * @param file - process image file. + * @param command_line - command line arguments for the process. + * @param environment - map of environment variables for the process, + * if null then default set of environment variables will be used. + * @param attach - if true debugger should be attached to the process. + * @param done - call back interface called when operation is completed. + * @return pending command handle, can be used to cancel the command. + */ + IToken start(String directory, String file, + String[] command_line, Map<String,String> environment, + boolean attach, DoneStart done); + + interface DoneStart { + void doneStart(IToken token, Exception error, ProcessContext process); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRegisters.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRegisters.java new file mode 100644 index 000000000..2cf4de1f4 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRegisters.java @@ -0,0 +1,318 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * IRegisters service provides access to target CPU register values and properties. + */ +public interface IRegisters extends IService { + + static final String NAME = "Registers"; + + /** + * Context property names. + */ + static final String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_PROCESS_ID = "ProcessID", + PROP_NAME = "Name", + PROP_DESCRIPTION = "Description", + PROP_FORMATS = "Formats", + PROP_READBLE = "Readable", + PROP_READ_ONCE = "ReadOnce", + PROP_WRITEABLE = "Writeable", + PROP_WRITE_ONCE = "WriteOnce", + PROP_SIDE_EFFECTS = "SideEffects", + PROP_VOLATILE = "Volatile", + PROP_FLOAT = "Float", + PROP_BIG_ENDIAN = "BigEndian", + PROP_LEFT_TO_RIGHT = "LeftToRight", + PROP_FIST_BIT = "FirstBit", + PROP_BITS = "Bits", + PROP_VALUES = "Values"; + + /** + * Standard known formats for register data. + */ + static final String + FORMAT_BINARY = "Binary", + FORMAT_OCTAL = "Octal", + FORMAT_DECIMAL = "Decimal", + FORMAT_HEX = "Hex", + FORMAT_NATURAL = "Natural"; + + /** + * Retrieve context info for given context ID. + * + * @param id – context ID. + * @param done - call back interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client call back interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, RegistersContext context); + } + + /** + * Retrieve contexts available for registers commands. + * A context corresponds to an execution thread, stack frame, registers group, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getChildren commands. + * @param done - call back interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client call back interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * RegistersContext objects represent register groups, registers and bit fields. + */ + interface RegistersContext { + /** + * Get Context ID. + * @return context ID. + */ + String getID(); + + /** + * Get parent context ID. + * @return parent context ID. + */ + String getParentID(); + + /** + * Get context (register, register group, bit field) name. + * @return context name. + */ + String getName(); + + /** + * Get context description. + * @return context description. + */ + String getDescription(); + + /** + * Get value formats available for register get/set commands. + * See FORMAT_* for knows format IDs definition. + * @return array of supported format IDs. + */ + String[] getAvailableFormats(); + + /** + * Check if context value can be read. + * @return true if can read value of the context. + */ + boolean isReadable(); + + /** + * Check if reading the context (register) destroys its current value - + * it can be read only once. + * @return true if read-once register. + */ + boolean isReadOnce(); + + /** + * Check if context value can be written. + * @return true if can write value of the context. + */ + boolean isWriteable(); + + /** + * Check if register value can not be overwritten - every write counts. + * @return true if write-once register. + */ + boolean isWriteOnce(); + + /** + * Check if writing the context can change values of other registers. + * @return true if has side effects. + */ + boolean hasSideEffects(); + + /** + * Check if the register value can change even when target is stopped. + * @return true if the register value can change at any time. + */ + boolean isVolatile(); + + /** + * Check if the register value is a floating-point value. + * @return true if a floating-point register. + */ + boolean isFloat(); + + /** + * Check endianess of the context. + * Big endian means decreasing numeric significance with increasing bit number. + * @return true if big endian. + */ + boolean isBigEndian(); + + /** + * Check if the lowest numbered bit (i.e. bit #0 or bit #1 depending on + * getFirstBitNumber() value) should be shown to user as the left-most bit or + * the right-most bit. + * @return true if the first bit is left-most bit. + */ + boolean isLeftToRight(); + + /** + * If the context has bit field children, bit positions of the fields + * can be zero-based or 1-based. + * @return first bit position - 0 or 1. + */ + int getFirstBitNumber(); + + /** + * If context is a bit field, get the field bit numbers in parent context. + * @return array of bit numbers. + */ + int[] getBitNumbers(); + + /** + * A context can have predefined names (mnemonics) for some its values. + * This method returns a list of such named values. + * @return array of named values or null. + */ + NamedValue[] getNamedValues(); + + /** + * Get complete map of context properties. + * @return map of context properties. + */ + Map<String,Object> getProperties(); + + /** + * Read value of the context. + * @param format - ID of a format to use for result value. + * @param done - call back object. + * @return - pending command handle. + */ + IToken get(String format, DoneGet done); + + /** + * Set value of the context. + * @param format - ID of a format used for value. + * @param value - value to write into the context. + * @param done - call back object. + * @return - pending command handle. + */ + IToken set(String format, String value, DoneSet done); + } + + /** + * A register context can have predefined names (mnemonics) for some its values. + * NamedValue objects represent such values. + */ + interface NamedValue { + /** + * Get number associated with this named value. + * @return the value as a number. + */ + Number getValue(); + + /** + * Get name (mnemonic) of the value. + * @return value name. + */ + String getName(); + + /** + * Get human readable description of the value. + * @return value description. + */ + String getDescription(); + } + + /** + * 'get' command call back interface. + */ + interface DoneGet { + void doneGet(IToken token, Exception error, String value); + } + + /** + * 'set' command call back interface. + */ + interface DoneSet { + void doneSet(IToken token, Exception error); + } + + /** + * Add registers service event listener. + * @param listener - event listener implementation. + */ + void addListener(RegistersListener listener); + + /** + * Remove registers service event listener. + * @param listener - event listener implementation. + */ + void removeListener(RegistersListener listener); + + /** + * Registers event listener is notified when registers context hierarchy + * changes, and when a register is modified by the service commands. + */ + interface RegistersListener { + + /** + * Called when register context properties changed. + * Most targets have static set of registers and register properties. + * Such targets never generate this event. However, some targets, + * for example, JTAG probes, allow user to modify register definitions. + * Clients should flush all cached register context data. + */ + void contextChanged(); + + /** + * Called when register content was changed and clients + * need to update themselves. Clients, at least, should invalidate + * corresponding cached registers data. + * Not every change is notified - it is not possible, + * only those, which are not caused by normal execution of the debuggee. + */ + void registerChanged(String context_id); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRunControl.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRunControl.java new file mode 100644 index 000000000..fc8befbcc --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IRunControl.java @@ -0,0 +1,323 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +public interface IRunControl extends IService { + + static final String NAME = "RunControl"; + + /** + * Context property names. + */ + static final String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_IS_CONTAINER = "IsContainer", + PROP_HAS_STATE = "HasState", + PROP_CAN_RESUME = "CanResume", + PROP_CAN_COUNT = "CanCount", + PROP_CAN_SUSPEND = "CanSuspend", + PROP_CAN_TERMINATE = "CanTerminate"; + + /** + * Context resume modes. + */ + static final int + RM_RESUME = 0, + RM_STEP_OVER = 1, + RM_STEP_INTO = 2, + RM_STEP_OVER_LINE = 3, + RM_STEP_INTO_LINE = 4, + RM_STEP_OUT = 5; + + /** + * State change reason of a context. + * Reason can be any text, but if it is one of predefined strings, + * a generic client might be able to handle it better. + */ + static final String + REASON_USER_REQUEST = "Suspended", + REASON_STEP = "Step", + REASON_BREAKPOINT = "Breakpoint", + REASON_EXCEPTION = "Exception", + REASON_CONTAINER = "Container", + REASON_WATCHPOINT = "Watchpoint", + REASON_SIGNAL = "Signal", + REASON_SHAREDLIB = "Shared Library", + REASON_ERROR = "Error"; + + /** + * Retrieve context properties for given context ID. + * + * @param id – context ID. + * @param done - callback interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client callback interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, RunControlContext context); + } + + /** + * Retrieve children of given context. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * @param done - callback interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client callback interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + */ + interface RunControlContext { + + /** + * Retrieve context ID. + * Same as getProperties().get(“ID”) + */ + String getID(); + + /** + * Retrieve parent context ID. + * Same as getProperties().get(“ParentID”) + */ + String getParentID(); + + /** + * Get context properties. See PROP_* definitions for property names. + * Context properties are read only, clients should not try to modify them. + * @return Map of context properties. + */ + Map<String,Object> getProperties(); + + /** + * Utility method to read context property PROP_IS_CONTAINER. + * Executing resume or suspend command on a container causes all its children to resume or suspend. + * @return value of PROP_IS_CONTAINER. + */ + boolean isContainer(); + + /** + * Utility method to read context property PROP_HAS_STATE. + * Only context that has a state can be resumed or suspended. + * @return value of PROP_HAS_STATE. + */ + boolean hasState(); + + /** + * Utility method to read context property PROP_CAN_SUSPEND. + * Value 'true' means suspend command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already suspended. + * @return value of PROP_CAN_SUSPEND. + */ + boolean canSuspend(); + + /** + * Utility method to read a 'mode' bit in context property PROP_CAN_RESUME. + * Value 'true' means resume command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already resumed. + * @param mode - resume mode, see RM_*. + * @return value of requested bit of PROP_CAN_RESUME. + */ + boolean canResume(int mode); + + /** + * Utility method to read a 'mode' bit in context property PROP_CAN_COUNT. + * Value 'true' means resume command with count other then 1 is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already resumed. + * @param mode - resume mode, see RM_*. + * @return value of requested bit of PROP_CAN_COUNT. + */ + boolean canCount(int mode); + + /** + * Utility method to read context property PROP_CAN_TERMINATE. + * Value 'true' means terminate command is supported by the context, + * however the method does not check that the command can be executed successfully in + * the current state of the context. For example, the command still can fail if context is + * already exited. + * @return value of PROP_CAN_SUSPEND. + */ + boolean canTerminate(); + + /** + * Send a command to retrieve current state of a context. + * @param done - command result call back object. + * @return pending command handle, can be used to cancel the command. + */ + IToken getState(DoneGetState done); + + /** + * Send a command to suspend a context. + * Also suspends children if context is a container. + * @param done - command result call back object. + * @return pending command handle, can be used to cancel the command. + */ + IToken suspend(DoneCommand done); + + /** + * Send a command to resume a context. + * Also resumes children if context is a container. + * @param mode - defines how to resume the context, see RM_*. + * @param count - if mode implies stepping, defines how many steps to perform. + * @param done - command result call back object. + * @return pending command handle, can be used to cancel the command. + */ + IToken resume(int mode, int count, DoneCommand done); + + /** + * Send a command to terminate a context. + * @param done - command result call back object. + * @return pending command handle, can be used to cancel the command. + */ + IToken terminate(DoneCommand done); + } + + class RunControlError extends Exception { + + private static final long serialVersionUID = 1L; + } + + interface DoneGetState { + void doneGetState(IToken token, Exception error, boolean suspended, String pc, + String reason, Map<String,Object> params); + } + + interface DoneCommand { + /** + * Called when run control command execution is complete. + * @param token - pending command handle. + * @param error - command execution error or null. + */ + void doneCommand(IToken token, Exception error); + } + + /** + * Add run control event listener. + * @param listener - run control event listener to add. + */ + void addListener(RunControlListener listener); + + /** + * Remove run control event listener. + * @param listener - run control event listener to remove. + */ + void removeListener(RunControlListener listener); + + /** + * Service events listener interface. + */ + interface RunControlListener { + + /** + * Called when new contexts are created. + * @param contexts - array of new context properties. + */ + void contextAdded(RunControlContext contexts[]); + + /** + * Called when a context properties changed. + * @param contexts - array of new context properties. + */ + void contextChanged(RunControlContext contexts[]); + + /** + * Called when contexts are removed. + * @param context_ids - aray of removed context IDs. + */ + void contextRemoved(String context_ids[]); + + /** + * Called when a thread is suspended. + * @param context - ID of a context that was suspended. + * @param pc - program counter of the context, can be null. + * @param reason - human readable description of suspend reason. + * @param params - additional, target specific data about suspended context. + */ + void contextSuspended(String context, String pc, + String reason, Map<String,Object> params); + + /** + * Called when a thread is resumed. + * @param context - ID of a context that was resumed. + */ + void contextResumed(String context); + + /** + * Called when target simultaneously suspends multiple threads in a container + * (process, core, etc.). + * + * @param context - ID of a context responsible for the event. It can be container ID or + * any one of container children, for example, it can be thread that hit "suspend all" breakpoint. + * Client expected to move focus (selection) to this context. + * @param pc - program counter of the context. + * @param reason - human readable description of suspend reason. + * @param params - additional target specific data about suspended context. + * @param suspended_ids - full list of all contexts that were suspended. + */ + void containerSuspended(String context, String pc, + String reason, Map<String,Object> params, String[] suspended_ids); + + /** + * Called when target simultaneously resumes multiple threads in a container (process, + * core, etc.). + * + * @param context_ids - full list of all contexts that were resumed. + */ + void containerResumed(String[] context_ids); + + /** + * Called when an exception is detected in a target thread. + * @param context - ID of a context that caused an exception. + * @param msg - human readable description of the exception. + */ + void contextException(String context, String msg); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IStackTrace.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IStackTrace.java new file mode 100644 index 000000000..a3657bcab --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/IStackTrace.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +public interface IStackTrace extends IService { + + static final String NAME = "StackTrace"; + + /** + * Context property names. + */ + static final String + PROP_ID = "ID", + PROP_PARENT_ID = "ParentID", + PROP_PROCESS_ID = "ProcessID", + PROP_NAME = "Name", + PROP_FRAME_ADDRESS = "FP", + PROP_RETURN_ADDRESS = "RP", + PROP_ARGUMENTS_COUNT = "ArgsCnt", + PROP_ARGUMENTS_ADDRESS = "ArgsAddr"; + + /** + * Retrieve context info for given context IDs. + * + * The command will fail if parent thread is not suspended. + * Client can use Run Control service to suspend a thread. + * + * @param id – array of context IDs. + * @param done - call back interface called when operation is completed. + */ + IToken getContext(String[] id, DoneGetContext done); + + /** + * Client call back interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – array of context data or null if error. + */ + void doneGetContext(IToken token, Exception error, StackTraceContext[] context); + } + + /** + * Retrieve stack trace context list. + * Parent context usually corresponds to an execution thread. + * Some targets have more then one stack. In such case children of a thread + * are stacks, and stack frames are deeper in the hierarchy - they can be + * retrieved with additional getChildren commands. + * + * The command will fail if parent thread is not suspended. + * Client can use Run Control service to suspend a thread. + * + * @param parent_context_id – parent context ID. + * @param done - call back interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client call back interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + * Stack frames are ordered from stack bottom to top. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * StackTraceContext represents stack trace objects - stacks and stack frames. + */ + interface StackTraceContext { + + /** + * Get Context ID. + * @return context ID. + */ + String getID(); + + /** + * Get parent context ID. + * @return parent context ID. + */ + String getParentID(); + + /** + * Get context name - if context represents a stack. + * @return context name or null. + */ + String getName(); + + /** + * Get memory address of this frame. + * @return address or null if not a stack frame. + */ + Number getFrameAddress(); + + /** + * Get program counter saved in this stack frame - + * it is address of instruction to be executed when the function returns. + * @return return address or null if not a stack frame. + */ + Number getReturnAddress(); + + /** + * Get number of function arguments for this frame. + * @return function arguments count. + */ + int getArgumentsCount(); + + /** + * Get address of function arguments area in memory. + * @return function arguments address or null if not available. + */ + Number getArgumentsAddress(); + + /** + * Get complete map of context properties. + * @return map of context properties. + */ + Map<String,Object> getProperties(); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ISysMonitor.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ISysMonitor.java new file mode 100644 index 000000000..f154c9c0e --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/services/ISysMonitor.java @@ -0,0 +1,378 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.services; + +import java.util.Map; + +import com.windriver.tcf.api.protocol.IService; +import com.windriver.tcf.api.protocol.IToken; + +/** + * This is optional service that can be implemented by a peer. + * If implemented, the service can be used for monitoring system activity and utilization. + * It provides list of running processes, different process attributes like command line, environment, etc., + * and some resource utilization data. The service can be used by a client to provide functionality + * similar to Unix 'top' utility or Windows 'Task Manager'. + */ +public interface ISysMonitor extends IService { + + static final String NAME = "SysMonitor"; + + /** + * Retrieve context info for given context ID. + * + * @param id – context ID. + * @param done - callback interface called when operation is completed. + */ + IToken getContext(String id, DoneGetContext done); + + /** + * Client callback interface for getContext(). + */ + interface DoneGetContext { + /** + * Called when context data retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context – context data. + */ + void doneGetContext(IToken token, Exception error, SysMonitorContext context); + } + + /** + * Retrieve children of given context. + * + * @param parent_context_id – parent context ID. Can be null – + * to retrieve top level of the hierarchy, or one of context IDs retrieved + * by previous getContext or getChildren commands. + * @param done - callback interface called when operation is completed. + */ + IToken getChildren(String parent_context_id, DoneGetChildren done); + + /** + * Client callback interface for getChildren(). + */ + interface DoneGetChildren { + /** + * Called when context list retrieval is done. + * @param error – error description if operation failed, null if succeeded. + * @param context_ids – array of available context IDs. + */ + void doneGetChildren(IToken token, Exception error, String[] context_ids); + } + + /** + * Context property names. + */ + static final String + /** The TCF context ID */ + PROP_ID = "ID", + + /** The TCF parent context ID */ + PROP_PARENTID = "ParentID", + + /** Current working directory of the process */ + PROP_CWD = "CWD", + + /** The process's root directory (as set by chroot) */ + PROP_ROOT = "Root", + + /** User ID of the process owner */ + PROP_UID = "UID", + + /** Group ID of the process owner */ + PROP_UGID = "UGID", + + /** User name of the process owner */ + PROP_USERNAME = "UserName", + + /** Group name of the process owner */ + PROP_GROUPNAME = "GroupName", + + /** System process ID */ + PROP_PID = "PID", + + /** Executable file of the process */ + PROP_FILE = "File", + + /** One character from the string "RSDZTW" where R is running, S is + * sleeping in an interruptible wait, D is waiting in uninterruptible + * disk sleep, Z is zombie, T is traced or stopped (on a signal), and W + * is paging.*/ + PROP_STATE = "State", + + /** System ID of the parent process */ + PROP_PPID = "PPID", + + /** The process group ID of the process */ + PROP_PGRP = "PGRP", + + /** The session ID of the process */ + PROP_SESSION = "Session", + + /** The tty the process uses */ + PROP_TTY = "TTY", + + /** The process group ID of the process which currently owns the tty that + * the process is connected to. */ + PROP_TGID = "TGID", + + /** ID of a process that has attached this process for tracing or debugging */ + PROP_TRACERPID = "TracerPID", + + /** The kernel flags word of the process. Details depend on the kernel */ + PROP_FLAGS = "Flags", + + /** The number of minor faults the process has made which have not + * required loading a memory page from disk */ + PROP_MINFLT = "MinFlt", + + /** The number of minor faults that the process's waited-for children have made */ + PROP_CMINFLT = "CMinFlt", + + /** The number of major faults the process has made which have required + * loading a memory page from disk */ + PROP_MAJFLT = "MajFlt", + + /** The number of major faults that the process's waited-for children + * have made */ + PROP_CMAJFLT = "CMajFlt", + + /** The number of milliseconds that this process has been scheduled in user mode */ + PROP_UTIME = "UTime", + + /** The number of milliseconds that this process has been scheduled in kernel mode */ + PROP_STIME = "STime", + + /** The number of jiffies that this process's waited-for children have + * been scheduled in user mode */ + PROP_CUTIME = "CUTime", + + /** The number of jiffies that this process's waited-for children have + * been scheduled in user mode */ + PROP_CSTIME = "CSTime", + + /** The standard nice value */ + PROP_PRIORITY = "Priority", + + /** The nice value */ + PROP_NICE = "Nice", + + /** The time in milliseconds before the next SIGALRM is sent to the process + * due to an interval timer */ + PROP_ITREALVALUE = "ITRealValue", + + /** The time in milliseconds the process started after system boot */ + PROP_STARTTIME = "StartTime", + + /** Virtual memory size in bytes */ + PROP_VSIZE = "VSize", + + /** Memory pages size in bytes */ + PROP_PSIZE = "PSize", + + /** Resident Set Size: number of pages the process has in real memory, + * minus used for administrative purposes. This is just the pages which + * count towards text, data, or stack space. This does not include + * pages which have not been demand-loaded in, or which are swapped out */ + PROP_RSS = "RSS", + + /** Current limit in bytes on the rss of the process */ + PROP_RLIMIT = "RLimit", + + /** The address above which program text can run */ + PROP_CODESTART = "CodeStart", + + /** The address below which program text can run */ + PROP_CODEEND = "CodeEnd", + + /** The address of the start of the stack */ + PROP_STACKSTART = "StackStart", + + /** The bitmap of pending signals */ + PROP_SIGNALS = "Signals", + + /** The bitmap of blocked signals */ + PROP_SIGBLOCK = "SigBlock", + + /** The bitmap of ignored signals */ + PROP_SIGIGNORE = "SigIgnore", + + /** The bitmap of caught signals */ + PROP_SIGCATCH = "SigCatch", + + /** This is the "channel" in which the process is waiting. It is the + * address of a system call, and can be looked up in a name list if you + * need a textual name */ + PROP_WCHAN = "WChan", + + /** Number of pages swapped */ + PROP_NSWAP = "NSwap", + + /** Cumulative NSwap for child processes */ + PROP_CNSWAP = "CNSwap", + + /** Signal to be sent to parent when this process exits */ + PROP_EXITSIGNAL = "ExitSignal", + + /** CPU number last executed on */ + PROP_PROCESSOR = "Processor", + + /** Real-time scheduling priority */ + PROP_RTPRIORITY = "RTPriority", + + /** Scheduling policy */ + PROP_POLICY = "Policy"; + + + /** + * A context corresponds to an execution thread, process, address space, etc. + * A context can belong to a parent context. Contexts hierarchy can be simple + * plain list or it can form a tree. It is up to target agent developers to choose + * layout that is most descriptive for a given target. Context IDs are valid across + * all services. In other words, all services access same hierarchy of contexts, + * with same IDs, however, each service accesses its own subset of context's + * attributes and functionality, which is relevant to that service. + */ + interface SysMonitorContext { + + /** + * Get context ID. + * Same as getProperties().get(“ID”) + */ + String getID(); + + /** + * Get parent context ID. + * Same as getProperties().get(“ParentID”) + */ + String getParentID(); + + /** + * Get process group ID. + * Same as getProperties().get(“PGRP”) + */ + long getPGRP(); + + /** + * Get process ID. + * Same as getProperties().get(“PID”) + */ + long getPID(); + + /** + * Get process parent ID. + * Same as getProperties().get(“PPID”) + */ + long getPPID(); + + /** + * Get process TTY group ID. + * Same as getProperties().get(“TGID”) + */ + long getTGID(); + + /** + * Get tracer process ID. + * Same as getProperties().get(“TracerPID”) + */ + long getTracerPID(); + + /** + * Get process owner user ID. + * Same as getProperties().get(“UID”) + */ + long getUID(); + + /** + * Get process owner user name. + * Same as getProperties().get(“UserName”) + */ + String getUserName(); + + /** + * Get process owner user group ID. + * Same as getProperties().get(“UGID”) + */ + long getUGID(); + + /** + * Get process owner user group name. + * Same as getProperties().get(“GroupName”) + */ + String getGroupName(); + + /** + * Get process state. + * Same as getProperties().get(“State”) + */ + String getState(); + + /** + * Get process virtual memory size in bytes. + * Same as getProperties().get(“VSize”) + */ + long getVSize(); + + /** + * Get process virtual memory page size in bytes. + * Same as getProperties().get(“PSize”) + */ + long getPSize(); + + /** + * Get number of memory pages in process resident set. + * Same as getProperties().get(“RSS”) + */ + long getRSS(); + + /** + * Get context executable file. + * Same as getProperties().get(“File”) + */ + String getFile(); + + /** + * Get context current file system root. + * Same as getProperties().get(“Root”) + */ + String getRoot(); + + /** + * Get context current working directory. + * Same as getProperties().get(“CWD”) + */ + String getCurrentWorkingDirectory(); + + /** + * Get all available context properties. + * @return Map 'property name' -> 'property value' + */ + Map<String,Object> getProperties(); + } + + /** + * Get context command line. + */ + IToken getCommandLine(String id, DoneGetCommandLine done); + + interface DoneGetCommandLine { + void doneGetCommandLine(IToken token, Exception error, String[] cmd_line); + } + + /** + * Get context environment variables. + */ + IToken getEnvironment(String id, DoneGetEnvironment done); + + interface DoneGetEnvironment { + void doneGetEnvironment(IToken token, Exception error, String[] environment); + } +} diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileInputStream.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileInputStream.java new file mode 100644 index 000000000..da81ce5e0 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileInputStream.java @@ -0,0 +1,246 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.LinkedList; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IFileSystem; +import com.windriver.tcf.api.services.IFileSystem.FileSystemException; +import com.windriver.tcf.api.services.IFileSystem.IFileHandle; + +/** + * TCFFileInputStream is high performance InputStream implementation over TCF FileSystem service. + * The class uses read-ahead buffers to achieve maximum throughput. + */ +public final class TCFFileInputStream extends InputStream { + + private static final int BUF_SIZE = 0x1000; + private static final int MAX_READ_AHEAD = 8; + + private static class Buffer { + + final long seq; + + IToken token; + byte[] buf; + boolean eof; + + Buffer(long seq) { + this.seq = seq; + } + } + + private final IFileHandle handle; + private long mark = 0; + private Buffer buf; + private int buf_pos = 0; + private long buf_seq = 0; + private boolean closed = false; + + private boolean suspend_read_ahead; + private Runnable waiting_client; + private final LinkedList<Buffer> read_ahead_buffers = new LinkedList<Buffer>(); + + public TCFFileInputStream(IFileHandle handle) { + this.handle = handle; + } + + private void startReadAhead(Buffer prv) { + if (prv.eof) return; + if (suspend_read_ahead) return; + if (read_ahead_buffers.size() > 0) { + prv = read_ahead_buffers.getLast(); + if (prv.eof) return; + } + long seq = prv.seq + 1; + while (read_ahead_buffers.size() < MAX_READ_AHEAD) { + final Buffer buf = new Buffer(seq); + IFileSystem fs = handle.getService(); + buf.token = fs.read(handle, buf.seq * BUF_SIZE, BUF_SIZE, new IFileSystem.DoneRead() { + public void doneRead(IToken token, FileSystemException error, + byte[] data, boolean eof) { + assert buf.token == token; + assert read_ahead_buffers.contains(buf); + buf.token = null; + if (error == null) { + assert data != null && data.length <= BUF_SIZE; + assert eof || data.length == BUF_SIZE; + buf.buf = data; + buf.eof = eof; + startReadAhead(buf); + } + else { + suspend_read_ahead = true; + read_ahead_buffers.remove(buf); + } + if (waiting_client != null) { + Protocol.invokeLater(waiting_client); + waiting_client = null; + } + } + }); + read_ahead_buffers.add(buf); + seq++; + } + } + + private boolean stopReadAhead(Runnable done) { + suspend_read_ahead = true; + for (Iterator<Buffer> i = read_ahead_buffers.iterator(); i.hasNext();) { + Buffer buf = i.next(); + if (buf.token == null || buf.token.cancel()) i.remove(); + } + if (read_ahead_buffers.size() > 0) { + assert waiting_client == null; + waiting_client = done; + return false; + } + return true; + } + + @Override + public synchronized int read() throws IOException { + if (closed) throw new IOException("Stream is closed"); + while (buf == null || buf_pos >= buf.buf.length) { + if (buf != null && buf.eof) return -1; + long offset = buf_seq * BUF_SIZE + buf_pos; + buf_seq = offset / BUF_SIZE; + buf_pos = (int)(offset % BUF_SIZE); + buf = new TCFTask<Buffer>() { + public void run() { + assert waiting_client == null; + while (read_ahead_buffers.size() > 0) { + Buffer buf = read_ahead_buffers.getFirst(); + if (buf.seq == buf_seq) { + if (buf.token != null) { + waiting_client = this; + } + else { + read_ahead_buffers.remove(buf); + startReadAhead(buf); + done(buf); + } + return; + } + suspend_read_ahead = true; + if (buf.token != null && buf.token.cancel()) buf.token = null; + if (buf.token != null) { + waiting_client = this; + return; + } + read_ahead_buffers.remove(buf); + } + IFileSystem fs = handle.getService(); + fs.read(handle, buf_seq * BUF_SIZE, BUF_SIZE, new IFileSystem.DoneRead() { + public void doneRead(IToken token, FileSystemException error, + byte[] data, boolean eof) { + if (error != null) { + error(error); + return; + } + assert data != null && data.length <= BUF_SIZE; + assert eof || data.length == BUF_SIZE; + Buffer buf = new Buffer(buf_seq); + buf.buf = data; + buf.eof = eof; + if (!eof) { + suspend_read_ahead = false; + startReadAhead(buf); + } + done(buf); + } + }); + } + }.getIO(); + assert buf.token == null; + assert buf.eof || buf.buf.length == BUF_SIZE; + } + assert buf.seq == buf_seq; + return buf.buf[buf_pos++] & 0xff; + } + + @Override + public synchronized int read(final byte arr[], final int off, final int len) throws IOException { + if (closed) throw new IOException("Stream is closed"); + if (arr == null) throw new NullPointerException(); + if (off < 0 || len < 0 || len > arr.length - off) throw new IndexOutOfBoundsException(); + int pos = 0; + while (pos < len) { + if (buf != null && buf_pos < buf.buf.length) { + int buf_len = buf.buf.length; + int n = len - pos < buf_len - buf_pos ? len - pos : buf_len - buf_pos; + System.arraycopy(buf.buf, buf_pos, arr, off + pos, n); + pos += n; + buf_pos += n; + } + else { + int c = read(); + if (c == -1) { + if (pos == 0) return -1; + break; + } + arr[off + pos++] = (byte)c; + } + } + return pos; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized void reset() throws IOException { + if (closed) throw new IOException("Stream is closed"); + new TCFTask<Object>() { + public void run() { + if (!stopReadAhead(this)) return; + done(this); + } + }.getIO(); + buf_seq = mark / BUF_SIZE; + buf_pos = (int)(mark % BUF_SIZE); + if (buf != null && buf.seq != buf_seq) buf = null; + } + + @Override + public synchronized void mark(int readlimit) { + mark = buf_seq * BUF_SIZE + buf_pos; + } + + @Override + public synchronized void close() throws IOException { + if (closed) return; + new TCFTask<Object>() { + public void run() { + if (!stopReadAhead(this)) return; + assert read_ahead_buffers.isEmpty(); + IFileSystem fs = handle.getService(); + fs.close(handle, new IFileSystem.DoneClose() { + public void doneClose(IToken token, FileSystemException error) { + if (error != null) error(error); + else done(this); + } + }); + } + }.getIO(); + closed = true; + buf_seq = 0; + buf_pos = 0; + buf = null; + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileOutputStream.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileOutputStream.java new file mode 100644 index 000000000..486e119e6 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFFileOutputStream.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.util; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IFileSystem; +import com.windriver.tcf.api.services.IFileSystem.FileSystemException; +import com.windriver.tcf.api.services.IFileSystem.IFileHandle; + +/** + * TCFFileOutputStream is high performance OutputStream implementation over TCF FileSystem service. + * The class uses write-back buffers to achieve maximum throughput. + */ +public final class TCFFileOutputStream extends OutputStream { + + private static final int BUF_SIZE = 0x1000; + private static final int MAX_WRITE_BACK = 8; + + private final IFileHandle handle; + private final Set<IToken> write_commands = new HashSet<IToken>(); + private final int[] dirty = new int[1]; + private final byte[] buf = new byte[BUF_SIZE]; + private int buf_pos = 0; + private long offset = 0; + private IOException flush_error; + private boolean closed; + + public TCFFileOutputStream(IFileHandle handle) { + this.handle = handle; + } + + @Override + public synchronized void write(int b) throws IOException { + if (closed) throw new IOException("Stream is closed"); + if (buf_pos == BUF_SIZE) flush(); + buf[buf_pos++] = (byte)b; + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + if (len == 0) return; + if (b == null) throw new NullPointerException(); + if (off < 0 || off > b.length || len < 0 || + off + len > b.length || off + len < 0) + throw new IndexOutOfBoundsException(); + while (len > 0) { + if (buf_pos == BUF_SIZE) flush(); + if (buf_pos == 0 && len > BUF_SIZE) { + flush(b, off, len); + return; + } + int n = BUF_SIZE - buf_pos; + if (len < n) n = len; + System.arraycopy(b, off, buf, buf_pos, n); + off += n; + len -= n; + buf_pos += n; + } + } + + @Override + public synchronized void flush() throws IOException { + if (buf_pos == 0) return; + flush(buf, 0, buf_pos); + buf_pos = 0; + } + + private void flush(final byte[] buf, final int off, final int len) throws IOException { + synchronized (dirty) { + if (flush_error != null) throw flush_error; + while (dirty[0] >= MAX_WRITE_BACK) { + try { + dirty.wait(); + } + catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + new TCFTask<Object>() { + public void run() { + IFileSystem fs = handle.getService(); + write_commands.add(fs.write(handle, offset, buf, off, len, new IFileSystem.DoneWrite() { + public void doneWrite(IToken token, FileSystemException error) { + assert write_commands.contains(token); + write_commands.remove(token); + if (error != null) { + for (Iterator<IToken> i = write_commands.iterator(); i.hasNext();) { + if (i.next().cancel()) i.remove(); + } + } + synchronized (dirty) { + if (error != null && flush_error == null) flush_error = error; + dirty[0] = write_commands.size(); + dirty.notifyAll(); + } + } + })); + synchronized (dirty) { + dirty[0] = write_commands.size(); + } + done(this); + } + }.getIO(); + offset += len; + } + + @Override + public synchronized void close() throws IOException { + if (closed) return; + flush(); + synchronized (dirty) { + while (dirty[0] > 0) { + try { + dirty.wait(); + } + catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + new TCFTask<Object>() { + public void run() { + IFileSystem fs = handle.getService(); + fs.close(handle, new IFileSystem.DoneClose() { + public void doneClose(IToken token, FileSystemException error) { + if (error != null) error(error); + else done(this); + } + }); + } + }.getIO(); + closed = true; + } +}
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFTask.java b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFTask.java new file mode 100644 index 000000000..abda52d02 --- /dev/null +++ b/plugins/com.windriver.tcf.api/src/com/windriver/tcf/api/util/TCFTask.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.api.util; + +import java.io.IOException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.windriver.tcf.api.protocol.Protocol; + +public abstract class TCFTask<V> implements Runnable, Future<V> { + + private V result; + private Throwable error; + private boolean canceled; + + public TCFTask() { + Protocol.invokeLater(new Runnable() { + public void run() { + try { + TCFTask.this.run(); + } + catch (Throwable x) { + if (result == null && error == null) error(x); + } + } + }); + } + + public synchronized void done(V result) { + if (canceled) return; + assert Protocol.isDispatchThread(); + assert this.error == null; + assert this.result == null; + this.result = result; + notifyAll(); + } + + public synchronized void error(Throwable error) { + assert Protocol.isDispatchThread(); + if (canceled) return; + assert this.error == null; + assert this.result == null; + this.error = error; + //System.err.print("TCFTask exception: "); + //error.printStackTrace(); + notifyAll(); + } + + public synchronized boolean cancel(boolean mayInterruptIfRunning) { + if (isDone()) return false; + canceled = true; + error = new CancellationException(); + notifyAll(); + return true; + } + + public V get() throws InterruptedException, ExecutionException { + assert !Protocol.isDispatchThread(); + synchronized (this) { + if (!isDone()) wait(); + if (error instanceof ExecutionException) throw (ExecutionException)error; + if (error instanceof InterruptedException) throw (InterruptedException)error; + if (error != null) throw new ExecutionException(error); + return result; + } + } + + public V getE() { + try { + return get(); + } + catch (Throwable e) { + if (e instanceof Error) throw (Error)e; + throw new Error(e); + } + } + + public V getIO() throws IOException { + try { + return get(); + } + catch (Throwable e) { + if (e instanceof IOException) throw (IOException)e; + IOException y = new IOException(); + y.initCause(e); + throw y; + } + } + + public synchronized V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + unit.toNanos(timeout); + // TODO Auto-generated method stub + assert false; + return null; + } + + public synchronized boolean isCancelled() { + return canceled; + } + + public synchronized boolean isDone() { + return canceled || error != null || result != null; + } +} diff --git a/plugins/com.windriver.tcf.dsf.core/.classpath b/plugins/com.windriver.tcf.dsf.core/.classpath new file mode 100644 index 000000000..751c8f2e5 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/com.windriver.tcf.dsf.core/.project b/plugins/com.windriver.tcf.dsf.core/.project new file mode 100644 index 000000000..afac03b39 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.windriver.tcf.dsf.core</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/plugins/com.windriver.tcf.dsf.core/META-INF/MANIFEST.MF b/plugins/com.windriver.tcf.dsf.core/META-INF/MANIFEST.MF new file mode 100644 index 000000000..31b80627c --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: com.windriver.tcf.dsf.core;singleton:=true +Bundle-Version: 0.1.0 +Bundle-Activator: com.windriver.tcf.dsf.core.Activator +Bundle-Vendor: %providerName +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.debug.core, + org.eclipse.core.resources, + org.eclipse.dd.dsf, + org.eclipse.dd.dsf.debug, + com.windriver.tcf.api, + com.windriver.debug.tcf.core +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Eclipse-LazyStart: true +Export-Package: com.windriver.tcf.dsf.core.launch, + com.windriver.tcf.dsf.core.services diff --git a/plugins/com.windriver.tcf.dsf.core/about.html b/plugins/com.windriver.tcf.dsf.core/about.html new file mode 100755 index 000000000..6c5b3615b --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>January 10, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.dsf.core/build.properties b/plugins/com.windriver.tcf.dsf.core/build.properties new file mode 100644 index 000000000..e9863e281 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/plugins/com.windriver.tcf.dsf.core/plugin.properties b/plugins/com.windriver.tcf.dsf.core/plugin.properties new file mode 100644 index 000000000..250c86adf --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2007 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Wind River Systems - initial implementation +############################################################################### +pluginName = TCF/DSF Integration Core +providerName = Eclipse.org + diff --git a/plugins/com.windriver.tcf.dsf.core/plugin.xml b/plugins/com.windriver.tcf.dsf.core/plugin.xml new file mode 100644 index 000000000..272eed3aa --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/plugin.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + <extension + point="org.eclipse.debug.core.launchConfigurationTypes"> + <launchConfigurationType + sourceLocatorId="com.windriver.debug.tcf.SourceLocator" + name="DSF over TCF" + sourcePathComputerId="com.windriver.debug.tcf.SourcePathComputer" + delegate="com.windriver.tcf.dsf.core.launch.TCFDSFLaunchDelegate" + modes="debug" + id="com.windriver.tcf.dsf.LaunchConfigurationType"> + </launchConfigurationType> + </extension> +</plugin> diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/Activator.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/Activator.java new file mode 100644 index 000000000..e8153f7ef --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/Activator.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core; + +import org.eclipse.core.runtime.Plugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends Plugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.windriver.tcf.dsf.core"; + + // The shared instance + private static Activator plugin; + private static BundleContext bundle_context; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + bundle_context = context; + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + public static BundleContext getBundleContext() { + return bundle_context; + } +} diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFExecuter.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFExecuter.java new file mode 100644 index 000000000..94b3cc18e --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFExecuter.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core.launch; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeSet; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.Delayed; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.dd.dsf.concurrent.DsfExecutor; + +import com.windriver.tcf.api.protocol.Protocol; + +public class TCFDSFExecuter extends AbstractExecutorService implements DsfExecutor { + + private class ScheduledFutureTask<V> extends FutureTask<V> implements ScheduledFuture<V> { + + private long time; // Milliseconds + private final int id; + private final long period; // Milliseconds + + public ScheduledFutureTask(long delay, long period, Runnable runnable, V result) { + super(runnable, result); + time = System.currentTimeMillis() + delay; + id = sf_count++; + this.period = period; + } + + public ScheduledFutureTask(long delay, Callable<V> callable) { + super(callable); + time = System.currentTimeMillis() + delay; + id = sf_count++; + period = 0; + } + + public long getDelay(TimeUnit unit) { + return unit.convert(time - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + + public int compareTo(Delayed o) { + if (o == this) return 0; + ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)o; + if (time < x.time) return -1; + if (time > x.time) return +1; + if (id < x.id) return -1; + if (id > x.id) return +1; + assert false; + return 0; + } + + public void run() { + if (period == 0) { + super.run(); + } + else { + boolean ok = super.runAndReset(); + synchronized (TCFDSFExecuter.this) { + // Reschedule if not canceled and not shutdown + if (ok && !is_shutdown) { + time = period > 0 ? time + period : System.currentTimeMillis() - period; + queue.add(this); + notify(); + } + } + } + } + } + + private static int sf_count = 0; + private final TreeSet<ScheduledFutureTask<?>> queue = new TreeSet<ScheduledFutureTask<?>>(); + private final Thread thread; + private boolean is_shutdown; + private boolean is_terminated; + + public TCFDSFExecuter() { + thread = new Thread(new Runnable() { + public void run() { + synchronized (TCFDSFExecuter.this) { + try { + while (true) { + if (queue.isEmpty()) { + if (is_shutdown) break; + TCFDSFExecuter.this.wait(); + } + else { + long time = System.currentTimeMillis(); + ScheduledFutureTask<?> s = queue.first(); + if (s.time <= time) { + queue.remove(s); + Protocol.invokeLater(s); + } + else { + TCFDSFExecuter.this.wait(s.time - time); + } + } + } + } + catch (Throwable x) { + x.printStackTrace(); + } + is_terminated = true; + } + } + }); + thread.setName("TCF Future Task Scheduler"); + thread.start(); + } + + public boolean isInExecutorThread() { + return Protocol.isDispatchThread(); + } + + public synchronized ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { + if (command == null || unit == null) throw new NullPointerException(); + if (is_shutdown) throw new RejectedExecutionException(); + delay = unit.toMillis(delay); + ScheduledFutureTask<Boolean> s = new ScheduledFutureTask<Boolean>(delay, 0, command, Boolean.TRUE); + queue.add(s); + notify(); + return s; + } + + public synchronized <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { + if (callable == null || unit == null) throw new NullPointerException(); + if (is_shutdown) throw new RejectedExecutionException(); + delay = unit.toMillis(delay); + ScheduledFutureTask<V> s = new ScheduledFutureTask<V>(delay, callable); + queue.add(s); + notify(); + return s; + } + + public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, + long initialDelay, long period, TimeUnit unit) { + if (command == null || unit == null) throw new NullPointerException(); + if (is_shutdown) throw new RejectedExecutionException(); + if (period <= 0) throw new RejectedExecutionException(); + ScheduledFutureTask<Boolean> s = new ScheduledFutureTask<Boolean>( + unit.toMillis(initialDelay), unit.toMillis(period), command, Boolean.TRUE); + queue.add(s); + notify(); + return s; + } + + public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, + long initialDelay, long delay, TimeUnit unit) { + if (command == null || unit == null) throw new NullPointerException(); + if (is_shutdown) throw new RejectedExecutionException(); + if (delay <= 0) throw new RejectedExecutionException(); + ScheduledFutureTask<Boolean> s = new ScheduledFutureTask<Boolean>( + unit.toMillis(initialDelay), -unit.toMillis(delay), command, Boolean.TRUE); + queue.add(s); + notify(); + return s; + } + + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + thread.join(unit.toMillis(timeout)); + return is_terminated; + } + + public synchronized boolean isShutdown() { + return is_shutdown; + } + + public synchronized boolean isTerminated() { + return is_terminated; + } + + public synchronized void shutdown() { + is_shutdown = true; + notify(); + } + + public synchronized List<Runnable> shutdownNow() { + List<Runnable> res = new ArrayList<Runnable>(queue); + queue.clear(); + is_shutdown = true; + notify(); + return res; + } + + public synchronized void execute(Runnable command) { + if (is_shutdown) throw new RejectedExecutionException(); + Protocol.invokeLater(command); + } +} diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFLaunch.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFLaunch.java new file mode 100644 index 000000000..f9535ce5e --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFLaunch.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core.launch; + +import org.eclipse.dd.dsf.concurrent.DsfExecutor; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.debug.core.ILaunchConfiguration; + +import com.windriver.debug.tcf.core.model.ITCFConstants; +import com.windriver.debug.tcf.core.model.TCFLaunch; + +public class TCFDSFLaunch extends TCFLaunch { + + private final TCFDSFExecuter executor; + private final DsfSession session; + + public TCFDSFLaunch(ILaunchConfiguration launchConfiguration, String mode) { + super(launchConfiguration, mode); + executor = new TCFDSFExecuter(); + session = DsfSession.startSession(executor, ITCFConstants.ID_TCF_DEBUG_MODEL); + } + + public DsfExecutor getDsfExecutor() { + return executor; + } + + public DsfSession getSession() { + return session; + } +} diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFLaunchDelegate.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFLaunchDelegate.java new file mode 100644 index 000000000..a672e46e4 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/launch/TCFDSFLaunchDelegate.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core.launch; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; + +import com.windriver.debug.tcf.core.launch.TCFLaunchDelegate; + +public class TCFDSFLaunchDelegate extends TCFLaunchDelegate { + + public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException { + return new TCFDSFLaunch(configuration, mode); + } +} diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFExecutionDMC.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFExecutionDMC.java new file mode 100644 index 000000000..b648df9dc --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFExecutionDMC.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core.services; + +import org.eclipse.dd.dsf.datamodel.AbstractDMContext; +import org.eclipse.dd.dsf.datamodel.IDMContext; +import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.dd.dsf.service.IDsfService; + +public abstract class TCFDSFExecutionDMC extends AbstractDMContext implements IExecutionDMContext, IContainerDMContext { + + TCFDSFExecutionDMC(IDsfService service, IDMContext[] parents) { + super(service, parents); + } + + public abstract String getTcfContextId(); +} diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFNativeProcesses.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFNativeProcesses.java new file mode 100644 index 000000000..5c1235ae6 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFNativeProcesses.java @@ -0,0 +1,332 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core.services; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.dsf.datamodel.IDMContext; +import org.eclipse.dd.dsf.datamodel.ServiceDMContext; +import org.eclipse.dd.dsf.debug.service.INativeProcesses; +import org.eclipse.dd.dsf.service.AbstractDsfService; +import org.eclipse.dd.dsf.service.DsfSession; +import org.osgi.framework.BundleContext; + +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.IProcesses; +import com.windriver.tcf.api.services.IProcesses.ProcessContext; +import com.windriver.tcf.dsf.core.Activator; + +public class TCFDSFNativeProcesses extends AbstractDsfService implements INativeProcesses { + + private class ProcessDMC extends TCFDSFProcessDMC { + + final String id; + + ProcessDMC(String id) { + super(TCFDSFNativeProcesses.this, new IDMContext[0]); + this.id = id; + } + + @Override + public String toString() { + return baseToString() + ".context[" + id + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && ((ProcessDMC)obj).id.equals(id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + } + + private class ThreadDMC extends TCFDSFThreadDMC { + + final String id; + + ThreadDMC(String id) { + super(TCFDSFNativeProcesses.this, new IDMContext[0]); + this.id = id; + } + + @Override + public String toString() { + return baseToString() + ".context[" + id + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && ((ThreadDMC)obj).id.equals(id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + } + + private static class ProcessData implements IProcessDMData { + + private final IProcesses.ProcessContext ctx; + + ProcessData(IProcesses.ProcessContext ctx) { + this.ctx = ctx; + } + + public IDMContext getDebugContext() { + // TODO Auto-generated method stub + assert false; + return null; + } + + public String getId() { + return ctx.getID(); + } + + public String getName() { + return ctx.getName(); + } + + public boolean isDebuggerAttached() { + return ctx.isAttached(); + } + + public boolean isValid() { + return true; + } + } + + private static class ThreadData implements IThreadDMData { + + private final IProcesses.ProcessContext ctx; + + ThreadData(IProcesses.ProcessContext ctx) { + this.ctx = ctx; + } + + public IDMContext getDebugContext() { + // TODO Auto-generated method stub + assert false; + return null; + } + + public String getId() { + return ctx.getID(); + } + + public String getName() { + return ctx.getName(); + } + + public boolean isDebuggerAttached() { + return ctx.isAttached(); + } + + public boolean isValid() { + return true; + } + + } + + final IProcesses service; + private IDMContext service_dmc; + + TCFDSFNativeProcesses(DsfSession session, IChannel channel) { + super(session); + service = channel.getRemoteService(IProcesses.class); + service_dmc = new ServiceDMContext(this, "#native_process"); + } + + @Override + protected BundleContext getBundleContext() { + return Activator.getBundleContext(); + } + + public IDMContext getServiceContext() { + return service_dmc; + } + + public boolean isValid() { + return true; + } + + public void attachDebuggerToProcess(IProcessDMContext ctx, RequestMonitor rm) { + // TODO Auto-generated method stub + assert false; + } + + public void canTerminate(IDMContext ctx, DataRequestMonitor<Boolean> rm) { + // TODO Auto-generated method stub + assert false; + } + + public void terminate(IDMContext ctx, RequestMonitor requestMonitor) { + // TODO Auto-generated method stub + assert false; + } + + public void debugNewProcess(String file, DataRequestMonitor<IProcessDMContext> rm) { + // TODO Auto-generated method stub + assert false; + } + + public void runNewProcess(String file, DataRequestMonitor<IProcessDMContext> rm) { + // TODO Auto-generated method stub + assert false; + } + + public IProcessDMContext getProcessForDebugContext(IDMContext ctx) { + if (ctx instanceof IProcessDMContext) { + return (IProcessDMContext)ctx; + } + if (ctx instanceof TCFDSFExecutionDMC) { + String id = ((TCFDSFExecutionDMC)ctx).getTcfContextId(); + return new ProcessDMC(id); + } + return null; + } + + public IThreadDMContext getThreadForDebugContext(IDMContext ctx) { + if (ctx instanceof IThreadDMContext) { + return (IThreadDMContext)ctx; + } + if (ctx instanceof TCFDSFExecutionDMC) { + String id = ((TCFDSFExecutionDMC)ctx).getTcfContextId(); + return new ThreadDMC(id); + } + return null; + } + + public void getProcessesBeingDebugged(final DataRequestMonitor<IProcessDMContext[]> rm) { + final Collection<String> list = new ArrayList<String>(); + final Set<IToken> cmds = new HashSet<IToken>(); + final IProcesses.DoneGetChildren done = new IProcesses.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] context_ids) { + if (cmds.isEmpty()) return; + assert cmds.contains(token); + cmds.remove(token); + if (error != null) { + for (IToken t : cmds) t.cancel(); + cmds.clear(); + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + rm.done(); + } + else { + for (String id : context_ids) { + list.add(id); + cmds.add(service.getChildren(id, true, this)); + } + if (cmds.isEmpty()) createDMContexts(list, rm); + } + } + }; + cmds.add(service.getChildren(null, true, done)); + } + + public void getRunningProcesses(final DataRequestMonitor<IProcessDMContext[]> rm) { + final Collection<String> list = new ArrayList<String>(); + final Set<IToken> cmds = new HashSet<IToken>(); + final IProcesses.DoneGetChildren done = new IProcesses.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] context_ids) { + if (cmds.isEmpty()) return; + assert cmds.contains(token); + cmds.remove(token); + if (error != null) { + for (IToken t : cmds) t.cancel(); + cmds.clear(); + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + rm.done(); + } + else { + for (String id : context_ids) { + list.add(id); + cmds.add(service.getChildren(id, false, this)); + } + if (cmds.isEmpty()) createDMContexts(list, rm); + } + } + }; + cmds.add(service.getChildren(null, false, done)); + } + + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, final DataRequestMonitor<?> rm) { + if (dmc instanceof ProcessDMC) { + service.getContext(((ProcessDMC)dmc).id, new IProcesses.DoneGetContext() { + + @SuppressWarnings("unchecked") + public void doneGetContext(IToken token, Exception error, ProcessContext context) { + if (error != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Data error", error)); //$NON-NLS-1$ + } + else { + ((DataRequestMonitor<IProcessDMData>)rm).setData(new ProcessData(context)); + } + rm.done(); + } + }); + } + else if (dmc instanceof ThreadDMC) { + service.getContext(((ProcessDMC)dmc).id, new IProcesses.DoneGetContext() { + + @SuppressWarnings("unchecked") + public void doneGetContext(IToken token, Exception error, ProcessContext context) { + if (error != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Data error", error)); //$NON-NLS-1$ + } + else { + ((DataRequestMonitor<IThreadDMData>)rm).setData(new ThreadData(context)); + } + rm.done(); + } + }); + } + else if (dmc == service_dmc) { + ((DataRequestMonitor<TCFDSFNativeProcesses>)rm).setData(this); + rm.done(); + } + else { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + private void createDMContexts(Collection<String> ids, DataRequestMonitor<IProcessDMContext[]> rm) { + assert false; + } + + public void getProcessData(IProcessDMContext dmc, + DataRequestMonitor<IProcessDMData> rm) { + // TODO Auto-generated method stub + + } + + public void getThreadData(IThreadDMContext dmc, DataRequestMonitor<IThreadDMData> rm) { + // TODO Auto-generated method stub + assert false; + } +} diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFProcessDMC.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFProcessDMC.java new file mode 100644 index 000000000..427fb92bf --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFProcessDMC.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core.services; + +import org.eclipse.dd.dsf.datamodel.AbstractDMContext; +import org.eclipse.dd.dsf.datamodel.IDMContext; +import org.eclipse.dd.dsf.debug.service.INativeProcesses.IProcessDMContext; +import org.eclipse.dd.dsf.service.IDsfService; + +public abstract class TCFDSFProcessDMC extends AbstractDMContext implements IProcessDMContext { + + TCFDSFProcessDMC(IDsfService service, IDMContext[] parents) { + super(service, parents); + } +} diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFRunControl.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFRunControl.java new file mode 100644 index 000000000..3564e25cf --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFRunControl.java @@ -0,0 +1,829 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core.services; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.dsf.datamodel.AbstractDMEvent; +import org.eclipse.dd.dsf.datamodel.IDMContext; +import org.eclipse.dd.dsf.datamodel.ServiceDMContext; +import org.eclipse.dd.dsf.service.AbstractDsfService; +import org.eclipse.dd.dsf.service.DsfSession; +import org.osgi.framework.BundleContext; + +import com.windriver.tcf.dsf.core.Activator; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IRunControl; +import com.windriver.tcf.api.services.IRunControl.RunControlContext; + +public class TCFDSFRunControl extends AbstractDsfService implements org.eclipse.dd.dsf.debug.service.IRunControl { + + public static class SuspendedEvent extends AbstractDMEvent<IExecutionDMContext> implements ISuspendedDMEvent { + + private final StateChangeReason reason; + + public SuspendedEvent(IExecutionDMContext dmc, String reason) { + super(dmc); + this.reason = toStateChangeReason(reason); + } + + public StateChangeReason getReason() { + return reason; + } + } + + public static class ResumedEvent extends AbstractDMEvent<IExecutionDMContext> implements IResumedDMEvent { + + public ResumedEvent(IExecutionDMContext dmc) { + super(dmc); + } + + public StateChangeReason getReason() { + return StateChangeReason.USER_REQUEST; + } + } + + public class ContainerSuspendedEvent extends AbstractDMEvent<IExecutionDMContext> implements IContainerSuspendedDMEvent { + + private final String trigger_id; + private final StateChangeReason reason; + + public ContainerSuspendedEvent(IExecutionDMContext dmc, String trigger_id, String reason) { + super(dmc); + this.trigger_id = trigger_id; + this.reason = toStateChangeReason(reason); + } + + public IExecutionDMContext getTriggeringContext() { + return model.get(trigger_id); + } + + public StateChangeReason getReason() { + return reason; + } + } + + public static class ContainerResumedEvent extends AbstractDMEvent<IExecutionDMContext> implements IContainerResumedDMEvent { + + public ContainerResumedEvent(IExecutionDMContext dmc) { + super(dmc); + } + + public StateChangeReason getReason() { + return StateChangeReason.USER_REQUEST; + } + } + + public static class StartedEvent extends AbstractDMEvent<IContainerDMContext> implements IStartedDMEvent { + + private final IExecutionDMContext exe; + + public StartedEvent(IContainerDMContext dmc, IExecutionDMContext exe) { + super(dmc); + this.exe = exe; + } + + public IExecutionDMContext getExecutionContext() { + return exe; + } + } + + public static class ChangedEvent extends AbstractDMEvent<IExecutionDMContext> { + + public ChangedEvent(IExecutionDMContext dmc) { + super(dmc); + } + } + + public static class ExitedEvent extends AbstractDMEvent<IContainerDMContext> implements IExitedDMEvent { + + private final IExecutionDMContext exe; + + public ExitedEvent(IContainerDMContext dmc, IExecutionDMContext exe) { + super(dmc); + this.exe = exe; + } + + public IExecutionDMContext getExecutionContext() { + return exe; + } + } + + private final com.windriver.tcf.api.services.IRunControl.RunControlListener run_listener = + new com.windriver.tcf.api.services.IRunControl.RunControlListener() { + + public void containerResumed(String[] context_ids) { + for (String id : context_ids) { + ExecutionDMC n = model.get(id); + if (n != null) n.onContextResumed(); + } + for (String id : context_ids) { + ExecutionDMC n = model.get(id); + if (n != null && n.ctx.isContainer()) { + getSession().dispatchEvent(new ContainerResumedEvent(n), getProperties()); + } + } + } + + public void containerSuspended(String trigger_id, String pc, + String reason, Map<String, Object> params, + String[] suspended_ids) { + if (trigger_id != null) { + ExecutionDMC n = model.get(trigger_id); + if (n != null) n.onContextSuspended(pc, reason, params); + } + for (String id : suspended_ids) { + if (id.equals(trigger_id)) continue; + ExecutionDMC n = model.get(id); + if (n != null) n.onContainerSuspended(reason); + } + for (String id : suspended_ids) { + ExecutionDMC n = model.get(id); + if (n != null && n.ctx.isContainer()) { + getSession().dispatchEvent(new ContainerSuspendedEvent(n, trigger_id, reason), getProperties()); + } + } + } + + public void contextAdded(RunControlContext[] contexts) { + for (RunControlContext ctx : contexts) { + ExecutionDMC n = model.get(ctx.getParentID()); + if (n != null) n.onContextAdded(ctx); + } + } + + public void contextChanged(RunControlContext[] contexts) { + for (RunControlContext ctx : contexts) { + ExecutionDMC n = model.get(ctx.getID()); + if (n != null) n.onContextChanged(ctx); + } + } + + public void contextException(String id, String msg) { + ExecutionDMC n = model.get(id); + if (n != null) n.onContextException(msg); + } + + public void contextRemoved(String[] context_ids) { + for (String id : context_ids) { + ExecutionDMC n = model.get(id); + if (n != null) n.onContextRemoved(); + } + } + + public void contextResumed(String id) { + ExecutionDMC n = model.get(id); + if (n != null) n.onContextResumed(); + } + + public void contextSuspended(String id, String pc, String reason, Map<String, Object> params) { + ExecutionDMC n = model.get(id); + if (n != null) n.onContextSuspended(pc, reason, params); + } + }; + + private interface IDataRequest { + void cancel(); + void done(); + } + + private static final int + VALID_CHILDREN = 4, + VALID_CONTEXT = 8, + VALID_STATE = 16, + VALID_ALL = VALID_CHILDREN | VALID_CONTEXT | VALID_STATE; + + private class ExecutionDMC extends TCFDSFExecutionDMC { + + final String id; + final ExecutionDMC parent; + + final SortedMap<String,ExecutionDMC> children = new TreeMap<String,ExecutionDMC>(); + final Map<String,ExecutionDMC> children_next = new HashMap<String,ExecutionDMC>(); + final Collection<IDataRequest> node_wait_list = new ArrayList<IDataRequest>(); + + int valid; + Throwable error; + boolean disposed; + IToken command; + + RunControlContext ctx; + int is_stepping; + int is_resuming; + boolean is_suspended; + boolean is_running; + String suspend_pc; + String suspend_reason; + String exception_msg; + Map<String,Object> suspend_params; + + public ExecutionDMC(ExecutionDMC parent, String id) { + super(TCFDSFRunControl.this, parent == null ? null : new IDMContext[] { parent }); + this.parent = parent; + this.id = id; + } + + @Override + public String toString() { + return baseToString() + ".context[" + id + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && ((ExecutionDMC)obj).id.equals(id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public String getTcfContextId() { + return id; + } + + void dispose() { + assert !disposed; + ExecutionDMC arr[] = children.values().toArray(new ExecutionDMC[children.size()]); + for (int i = 0; i < arr.length; i++) arr[i].dispose(); + assert children.isEmpty(); + if (parent != null) { + parent.children.remove(id); + parent.children_next.remove(id); + } + model.remove(id); + disposed = true; + } + + void invalidateDMC(int flags) { + // cancel current data retrieval command + if (command != null) { + command.cancel(); + command = null; + } + + // cancel waiting requests + if (!node_wait_list.isEmpty()) { + IDataRequest[] arr = node_wait_list.toArray(new IDataRequest[node_wait_list.size()]); + node_wait_list.clear(); + for (IDataRequest r : arr) r.cancel(); + } + + if ((flags & VALID_STATE) != 0) { + is_suspended = false; + is_running = false; + } + + if ((flags & VALID_CHILDREN) != 0) { + children_next.clear(); + for (ExecutionDMC n : children.values()) n.invalidateDMC(VALID_ALL); + } + + if (flags == VALID_ALL) { + error = null; + } + + valid &= ~flags; + } + + boolean validateDMC(IDataRequest done) { + assert Protocol.isDispatchThread(); + assert (valid & ~VALID_ALL) == 0; + assert parent == null || parent.children.get(id) == model.get(id); + if (channel.getState() != IChannel.STATE_OPEN) { + children_next.clear(); + error = null; + command = null; + valid = VALID_ALL; + } + if (command != null) { + if (done != null) node_wait_list.add(done); + return false; + } + if (parent != null && parent.error != null) { + valid = VALID_ALL; + } + if ((valid & VALID_CONTEXT) == 0 && !validateRunControlContext(done)) return false; + if ((valid & VALID_STATE) == 0 && !validateRunControlState(done)) return false; + if ((valid & VALID_CHILDREN) == 0 && !validateRunControlChildren(done)) return false; + assert valid == VALID_ALL; + assert command == null; + ExecutionDMC[] a = children.values().toArray(new ExecutionDMC[children.size()]); + for (ExecutionDMC n : a) { + if (children_next.get(n.id) == null) n.dispose(); + } + for (ExecutionDMC n : children_next.values()) { + if (children.get(n.id) == null) { + children.put(n.id, n); + model.put(n.id, n); + } + } + if (!node_wait_list.isEmpty()) { + IDataRequest[] arr = node_wait_list.toArray(new IDataRequest[node_wait_list.size()]); + node_wait_list.clear(); + for (IDataRequest r : arr) r.done(); + } + assert valid == VALID_ALL; + return true; + } + + private boolean validateRunControlChildren(IDataRequest done) { + assert command == null; + if (tcf_run_service == null) { + valid |= VALID_CHILDREN; + return true; + } + if (done != null) node_wait_list.add(done); + command = tcf_run_service.getChildren(id, new IRunControl.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + if (command != token) return; + command = null; + if (error != null) { + ExecutionDMC.this.error = error; + } + else { + for (int i = 0; i < contexts.length; i++) { + String id = contexts[i]; + ExecutionDMC node = model.get(id); + if (node == null) node = new ExecutionDMC(ExecutionDMC.this, id); + children_next.put(id, node); + } + } + valid |= VALID_CHILDREN; + validateDMC(null); + } + }); + return false; + } + + private boolean validateRunControlContext(IDataRequest done) { + assert command == null; + if (tcf_run_service == null) { + valid |= VALID_CONTEXT; + return true; + } + if (done != null) node_wait_list.add(done); + command = tcf_run_service.getContext(id, new IRunControl.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext ctx) { + if (command != token) return; + command = null; + if (error != null) { + ExecutionDMC.this.error = error; + } + else { + ExecutionDMC.this.ctx = ctx; + } + valid |= VALID_CONTEXT; + validateDMC(null); + } + }); + return false; + } + + private boolean validateRunControlState(IDataRequest done) { + assert command == null; + if (ctx == null) { + valid |= VALID_STATE; + return true; + } + if (error != null || !ctx.hasState()) { + is_running = false; + is_suspended = false; + suspend_pc = null; + suspend_reason = null; + suspend_params = null; + valid |= VALID_STATE; + return true; + } + if (done != null) node_wait_list.add(done); + command = ctx.getState(new IRunControl.DoneGetState() { + public void doneGetState(IToken token, Exception error, boolean suspend, String pc, String reason, Map<String,Object> params) { + if (token != command) return; + command = null; + if (error != null) { + is_running = false; + is_suspended = false; + suspend_pc = null; + suspend_reason = null; + suspend_params = null; + ExecutionDMC.this.error = error; + } + else { + is_running = !suspend; + is_suspended = suspend; + if (suspend) { + suspend_pc = pc; + suspend_reason = reason; + suspend_params = params; + } + else { + suspend_pc = null; + suspend_reason = null; + suspend_params = null; + } + } + valid |= VALID_STATE; + validateDMC(null); + } + }); + return false; + } + + /*--------------------------------------------------------------------------------------*/ + /* Events */ + + void onContextAdded(IRunControl.RunControlContext context) { + String id = context.getID(); + assert !disposed; + assert children.get(id) == null; + ExecutionDMC n = new ExecutionDMC(this, id); + n.ctx = context; + n.valid |= VALID_CONTEXT; + children.put(id, n); + model.put(id, n); + getSession().dispatchEvent(new StartedEvent(this, n), getProperties()); + } + + void onContextChanged(IRunControl.RunControlContext context) { + assert !disposed; + ctx = context; + invalidateDMC(VALID_CHILDREN); + getSession().dispatchEvent(new ChangedEvent(this), getProperties()); + } + + void onContextRemoved() { + assert !disposed; + dispose(); + getSession().dispatchEvent(new ExitedEvent(parent, this), getProperties()); + } + + void onContainerSuspended(String reason) { + assert !disposed; + if (ctx == null) return; + if (!ctx.hasState()) return; + invalidateDMC(VALID_STATE); + getSession().dispatchEvent(new SuspendedEvent(this, reason), getProperties()); + } + + void onContextSuspended(String pc, String reason, Map<String,Object> params) { + assert !disposed; + if (ctx == null) return; + assert ctx.hasState(); + is_suspended = true; + suspend_pc = pc; + suspend_reason = reason; + suspend_params = params; + is_running = false; + valid |= VALID_STATE; + getSession().dispatchEvent(new SuspendedEvent(this, reason), getProperties()); + } + + void onContextResumed() { + assert !disposed; + if (ctx == null) return; + assert ctx.hasState(); + exception_msg = null; + is_suspended = false; + suspend_pc = null; + suspend_reason = null; + suspend_params = null; + is_running = true; + valid |= VALID_STATE; + getSession().dispatchEvent(new ResumedEvent(this), getProperties()); + } + + void onContextException(String msg) { + assert !disposed; + exception_msg = msg; + } + } + + private static class ExecutionData implements IExecutionDMData { + + private final StateChangeReason reason; + + ExecutionData(StateChangeReason reason) { + this.reason = reason; + } + + public boolean isValid() { + return true; + } + + public StateChangeReason getStateChangeReason() { + return reason; + } + } + + private static StateChangeReason toStateChangeReason(String s) { + if (s == null) return StateChangeReason.UNKNOWN; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_USER_REQUEST)) return StateChangeReason.USER_REQUEST; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_STEP)) return StateChangeReason.STEP; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_BREAKPOINT)) return StateChangeReason.BREAKPOINT; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_EXCEPTION)) return StateChangeReason.EXCEPTION; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_CONTAINER)) return StateChangeReason.CONTAINER; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_WATCHPOINT)) return StateChangeReason.WATCHPOINT; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_SIGNAL)) return StateChangeReason.SIGNAL; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_SHAREDLIB)) return StateChangeReason.SHAREDLIB; + if (s.equals(com.windriver.tcf.api.services.IRunControl.REASON_ERROR)) return StateChangeReason.ERROR; + return StateChangeReason.UNKNOWN; + } + + private final IChannel channel; + private final com.windriver.tcf.api.services.IRunControl tcf_run_service; + private final Map<String,ExecutionDMC> model = new HashMap<String,ExecutionDMC>(); + private IDMContext service_dmc; + + public TCFDSFRunControl(DsfSession session, IChannel channel) { + super(session); + this.channel = channel; + tcf_run_service = channel.getRemoteService(com.windriver.tcf.api.services.IRunControl.class); + if (tcf_run_service != null) tcf_run_service.addListener(run_listener); + service_dmc = new ServiceDMContext(this, "#run_control"); + } + + @Override + protected BundleContext getBundleContext() { + return Activator.getBundleContext(); + } + + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, final DataRequestMonitor<?> rm) { + if (dmc instanceof ExecutionDMC) { + final ExecutionDMC ctx = (ExecutionDMC)dmc; + IDataRequest done = new IDataRequest() { + + public void cancel() { + rm.setCanceled(true); + rm.done(); + } + + @SuppressWarnings("unchecked") + public void done() { + if (ctx.error != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Data error", ctx.error)); //$NON-NLS-1$ + } + else { + ExecutionData dt = new ExecutionData(toStateChangeReason(ctx.suspend_reason)); + ((DataRequestMonitor<IExecutionDMData>)rm).setData(dt); + } + rm.done(); + } + }; + if (ctx.validateDMC(done)) done.done(); + } + else if (dmc == service_dmc) { + ((DataRequestMonitor<TCFDSFRunControl>)rm).setData(this); + rm.done(); + } + else { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public IDMContext getServiceContext() { + return service_dmc; + } + + public boolean isValid() { + return true; + } + + public IContainerDMContext getContainerDMC() { + // TODO: getContainerDMC() + assert false; + return null; + } + + public boolean canInstructionStep(IDMContext context) { + if (context instanceof ExecutionDMC) { + ExecutionDMC x = (ExecutionDMC)context; + return x.ctx.canResume(com.windriver.tcf.api.services.IRunControl.RM_STEP_INTO); + } + return false; + } + + public boolean canResume(IDMContext context) { + if (context instanceof ExecutionDMC) { + ExecutionDMC x = (ExecutionDMC)context; + return x.ctx.canResume(com.windriver.tcf.api.services.IRunControl.RM_RESUME); + } + return false; + } + + public boolean canStep(IDMContext context) { + if (context instanceof ExecutionDMC) { + ExecutionDMC x = (ExecutionDMC)context; + return x.ctx.canResume(com.windriver.tcf.api.services.IRunControl.RM_STEP_OVER); + } + return false; + } + + public boolean canSuspend(IDMContext context) { + if (context instanceof ExecutionDMC) { + ExecutionDMC x = (ExecutionDMC)context; + return x.ctx.canSuspend(); + } + return false; + } + + public void getExecutionContexts(IContainerDMContext context, final DataRequestMonitor<IExecutionDMContext[]> rm) { + if (context instanceof ExecutionDMC) { + final ExecutionDMC ctx = (ExecutionDMC)context; + IDataRequest done = new IDataRequest() { + + public void cancel() { + rm.setCanceled(true); + rm.done(); + } + + @SuppressWarnings("unchecked") + public void done() { + if (ctx.error != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Data error", ctx.error)); //$NON-NLS-1$ + } + else { + rm.setData(ctx.children.values().toArray(new ExecutionDMC[ctx.children.size()])); + } + rm.done(); + } + }; + if (ctx.validateDMC(done)) done.done(); + } + else { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void step(IDMContext context, StepType stepType, final RequestMonitor rm) { + if (context instanceof ExecutionDMC) { + final ExecutionDMC x = (ExecutionDMC)context; + int md = -1; + switch (stepType) { + case STEP_OVER: + md = com.windriver.tcf.api.services.IRunControl.RM_STEP_OVER_LINE; + break; + case STEP_INTO: + md = com.windriver.tcf.api.services.IRunControl.RM_STEP_INTO_LINE; + break; + case STEP_RETURN: + md = com.windriver.tcf.api.services.IRunControl.RM_STEP_OUT; + break; + } + if (md < 0) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + NOT_SUPPORTED, "Invalid step type", null)); //$NON-NLS-1$ + rm.done(); + } + else { + x.ctx.resume(md, 1, new com.windriver.tcf.api.services.IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + } + x.is_stepping--; + rm.done(); + } + }); + x.is_stepping++; + } + } + else { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void instructionStep(IDMContext context, StepType stepType, final RequestMonitor rm) { + if (context instanceof ExecutionDMC) { + final ExecutionDMC x = (ExecutionDMC)context; + int md = -1; + switch (stepType) { + case STEP_OVER: + md = com.windriver.tcf.api.services.IRunControl.RM_STEP_OVER; + break; + case STEP_INTO: + md = com.windriver.tcf.api.services.IRunControl.RM_STEP_INTO; + break; + case STEP_RETURN: + md = com.windriver.tcf.api.services.IRunControl.RM_STEP_OUT; + break; + } + if (md < 0) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + NOT_SUPPORTED, "Invalid step type", null)); //$NON-NLS-1$ + rm.done(); + } + else { + x.ctx.resume(md, 1, new com.windriver.tcf.api.services.IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + } + x.is_stepping--; + rm.done(); + } + }); + x.is_stepping++; + } + } + else { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public boolean isStepping(IDMContext context) { + if (context instanceof ExecutionDMC) { + ExecutionDMC x = (ExecutionDMC)context; + return x.is_stepping > 0; + } + return false; + } + + public void resume(IDMContext context, final RequestMonitor rm) { + if (context instanceof ExecutionDMC) { + final ExecutionDMC x = (ExecutionDMC)context; + x.ctx.resume(com.windriver.tcf.api.services.IRunControl.RM_RESUME, 1, + new com.windriver.tcf.api.services.IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + } + x.is_resuming--; + rm.done(); + } + }); + x.is_resuming++; + } + else { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void suspend(IDMContext context, final RequestMonitor rm) { + if (context instanceof ExecutionDMC) { + final ExecutionDMC x = (ExecutionDMC)context; + x.ctx.suspend(new com.windriver.tcf.api.services.IRunControl.DoneCommand() { + public void doneCommand(IToken token, Exception error) { + if (error != null) { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + REQUEST_FAILED, "Command error", error)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } + else { + rm.setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public boolean isSuspended(IDMContext context) { + if (context instanceof ExecutionDMC) { + ExecutionDMC x = (ExecutionDMC)context; + return x.is_suspended && x.is_resuming == 0 && x.is_stepping == 0; + } + return false; + } + + public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor<IExecutionDMData> rm) { + // TODO Auto-generated method stub + assert false; + } +} diff --git a/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFThreadDMC.java b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFThreadDMC.java new file mode 100644 index 000000000..ec91b6911 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.core/src/com/windriver/tcf/dsf/core/services/TCFDSFThreadDMC.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.core.services; + +import org.eclipse.dd.dsf.datamodel.AbstractDMContext; +import org.eclipse.dd.dsf.datamodel.IDMContext; +import org.eclipse.dd.dsf.debug.service.INativeProcesses.IThreadDMContext; +import org.eclipse.dd.dsf.service.IDsfService; + +public abstract class TCFDSFThreadDMC extends AbstractDMContext implements IThreadDMContext { + + public TCFDSFThreadDMC(IDsfService service, IDMContext[] parents) { + super(service, parents); + } +} diff --git a/plugins/com.windriver.tcf.dsf.ui/.classpath b/plugins/com.windriver.tcf.dsf.ui/.classpath new file mode 100644 index 000000000..751c8f2e5 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/com.windriver.tcf.dsf.ui/.project b/plugins/com.windriver.tcf.dsf.ui/.project new file mode 100644 index 000000000..77c9739b6 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.windriver.tcf.dsf.ui</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/plugins/com.windriver.tcf.dsf.ui/META-INF/MANIFEST.MF b/plugins/com.windriver.tcf.dsf.ui/META-INF/MANIFEST.MF new file mode 100644 index 000000000..a4b898b02 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: com.windriver.tcf.dsf.ui;singleton:=true +Bundle-Version: 0.1.0 +Bundle-Activator: com.windriver.tcf.dsf.ui.Activator +Bundle-Vendor: %providerName +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.dd.dsf.debug, + org.eclipse.dd.dsf.debug.ui, + org.eclipse.dd.dsf, + org.eclipse.dd.dsf.ui, + org.eclipse.debug.ui, + com.windriver.tcf.api, + com.windriver.debug.tcf.core, + com.windriver.debug.tcf.ui, + com.windriver.tcf.dsf.core +Eclipse-LazyStart: true +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/plugins/com.windriver.tcf.dsf.ui/about.html b/plugins/com.windriver.tcf.dsf.ui/about.html new file mode 100755 index 000000000..6c5b3615b --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>January 10, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.dsf.ui/build.properties b/plugins/com.windriver.tcf.dsf.ui/build.properties new file mode 100644 index 000000000..e9863e281 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/plugins/com.windriver.tcf.dsf.ui/icons/tcf.gif b/plugins/com.windriver.tcf.dsf.ui/icons/tcf.gif Binary files differnew file mode 100644 index 000000000..3198679ae --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/icons/tcf.gif diff --git a/plugins/com.windriver.tcf.dsf.ui/plugin.properties b/plugins/com.windriver.tcf.dsf.ui/plugin.properties new file mode 100644 index 000000000..85a170935 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2007 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Wind River Systems - initial implementation +############################################################################### +pluginName = TCF/DSF Integration UI +providerName = Eclipse.org + diff --git a/plugins/com.windriver.tcf.dsf.ui/plugin.xml b/plugins/com.windriver.tcf.dsf.ui/plugin.xml new file mode 100644 index 000000000..4da1ce1d0 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/plugin.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + + <extension point="com.windriver.debug.tcf.core.startup"/> + + <extension point="org.eclipse.debug.ui.launchConfigurationTabGroups"> + <launchConfigurationTabGroup + type="com.windriver.tcf.dsf.LaunchConfigurationType" + class="com.windriver.tcf.dsf.ui.LaunchDialogTabGroup" + id="org.eclipse.dd.dsf.mi.launch.localRunLaunchTabGroup"> + </launchConfigurationTabGroup> + </extension> + + <extension point="org.eclipse.debug.ui.launchConfigurationTypeImages"> + <launchConfigurationTypeImage + icon="icons/tcf.gif" + configTypeID="com.windriver.tcf.dsf.LaunchConfigurationType" + id="com.windriver.tcf.dsf.LaunchImage"> + </launchConfigurationTypeImage> + </extension> + + <extension point="org.eclipse.core.runtime.adapters"> + <factory + class="com.windriver.tcf.dsf.ui.AdapterFactory" + adaptableType="com.windriver.tcf.dsf.core.launch.TCFDSFLaunch"> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider"/> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider"/> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory"/> + <adapter type="org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory"/> + <adapter type="org.eclipse.debug.core.commands.ITerminateHandler"/> + </factory> + </extension> + +</plugin> diff --git a/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/Activator.java b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/Activator.java new file mode 100644 index 000000000..f37328701 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/Activator.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.ui; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.windriver.tcf.dsf.ui"; + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/AdapterFactory.java b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/AdapterFactory.java new file mode 100644 index 000000000..f8d0604f8 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/AdapterFactory.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.ui; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.dd.dsf.concurrent.Immutable; +import org.eclipse.dd.dsf.debug.ui.actions.DsfResumeCommand; +import org.eclipse.dd.dsf.debug.ui.actions.DsfStepIntoCommand; +import org.eclipse.dd.dsf.debug.ui.actions.DsfStepOverCommand; +import org.eclipse.dd.dsf.debug.ui.actions.DsfStepReturnCommand; +import org.eclipse.dd.dsf.debug.ui.actions.DsfSuspendCommand; +import org.eclipse.dd.dsf.debug.ui.actions.DsfTerminateCommand; +import org.eclipse.dd.dsf.debug.ui.sourcelookup.MISourceDisplayAdapter; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.core.commands.IResumeHandler; +import org.eclipse.debug.core.commands.IStepIntoHandler; +import org.eclipse.debug.core.commands.IStepOverHandler; +import org.eclipse.debug.core.commands.IStepReturnHandler; +import org.eclipse.debug.core.commands.ISuspendHandler; +import org.eclipse.debug.core.commands.ITerminateHandler; +import org.eclipse.debug.core.model.IDebugModelProvider; +import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; + +import com.windriver.debug.tcf.core.model.ITCFConstants; +import com.windriver.tcf.dsf.core.launch.TCFDSFLaunch; + +@SuppressWarnings("restriction") +public class AdapterFactory implements IAdapterFactory, DsfSession.SessionEndedListener, ILaunchesListener2 { + + @Immutable + private final class SessionAdapterSet { + + private final DsfSession fSession; + final ViewModelAdapter fViewModelAdapter; + final MISourceDisplayAdapter fSourceDisplayAdapter; + final DsfStepIntoCommand fStepIntoCommand; + final DsfStepOverCommand fStepOverCommand; + final DsfStepReturnCommand fStepReturnCommand; + final DsfSuspendCommand fSuspendCommand; + final DsfResumeCommand fResumeCommand; + final DsfTerminateCommand fTerminateCommand; + final IDebugModelProvider fDebugModelProvider; + final TCFDSFLaunch fLaunch; + + SessionAdapterSet(DsfSession session, TCFDSFLaunch launch) { + fSession = session; + + fViewModelAdapter = new ViewModelAdapter(session, launch); + + if (launch.getSourceLocator() instanceof ISourceLookupDirector) { + fSourceDisplayAdapter = new MISourceDisplayAdapter(session, (ISourceLookupDirector)launch.getSourceLocator()); + } else { + fSourceDisplayAdapter = null; + } + session.registerModelAdapter(ISourceDisplay.class, fSourceDisplayAdapter); + + fStepIntoCommand = new DsfStepIntoCommand(session); + fStepOverCommand = new DsfStepOverCommand(session); + fStepReturnCommand = new DsfStepReturnCommand(session); + fSuspendCommand = new DsfSuspendCommand(session); + fResumeCommand = new DsfResumeCommand(session); + fTerminateCommand = new DsfTerminateCommand(session); + session.registerModelAdapter(IStepIntoHandler.class, fStepIntoCommand); + session.registerModelAdapter(IStepOverHandler.class, fStepOverCommand); + session.registerModelAdapter(IStepReturnHandler.class, fStepReturnCommand); + session.registerModelAdapter(ISuspendHandler.class, fSuspendCommand); + session.registerModelAdapter(IResumeHandler.class, fResumeCommand); + session.registerModelAdapter(ITerminateHandler.class, fTerminateCommand); + + fDebugModelProvider = new IDebugModelProvider() { + // @see org.eclipse.debug.core.model.IDebugModelProvider#getModelIdentifiers() + public String[] getModelIdentifiers() { + return new String[] { ITCFConstants.ID_TCF_DEBUG_MODEL }; + } + }; + session.registerModelAdapter(IDebugModelProvider.class, fDebugModelProvider); + + fLaunch = launch; + + /* + * Registering the launch as an adapter, ensures that this launch, + * and debug model ID will be associated with all DMContexts from this + * session. + */ + session.registerModelAdapter(ILaunch.class, fLaunch); + } + + void dispose() { + fViewModelAdapter.dispose(); + + fSession.unregisterModelAdapter(ISourceDisplay.class); + if (fSourceDisplayAdapter != null) fSourceDisplayAdapter.dispose(); + + fSession.unregisterModelAdapter(IStepIntoHandler.class); + fSession.unregisterModelAdapter(IStepOverHandler.class); + fSession.unregisterModelAdapter(IStepReturnHandler.class); + fSession.unregisterModelAdapter(ISuspendHandler.class); + fSession.unregisterModelAdapter(IResumeHandler.class); + fSession.unregisterModelAdapter(ITerminateHandler.class); + fStepIntoCommand.dispose(); + fStepOverCommand.dispose(); + fStepReturnCommand.dispose(); + fSuspendCommand.dispose(); + fResumeCommand.dispose(); + fTerminateCommand.dispose(); + } + } + + @SuppressWarnings({ "unchecked", "restriction" }) + private final Class[] adapter_list = { + IElementLabelProvider.class, + IElementContentProvider.class, + IColumnPresentationFactory.class, + IModelProxyFactory.class, + ITerminateHandler.class + }; + + private Map<String,SessionAdapterSet> fSessionAdapterSetMap = + Collections.synchronizedMap(new HashMap<String,SessionAdapterSet>()); + + public AdapterFactory() { + DsfSession.addSessionEndedListener(this); + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); + } + + @SuppressWarnings({ "restriction", "unchecked" }) + public Object getAdapter(Object adaptableObject, Class adapterType) { + if (!(adaptableObject instanceof TCFDSFLaunch)) return null; + + TCFDSFLaunch launch = (TCFDSFLaunch)adaptableObject; + + // Find the correct set of adapters based on the launch session-ID. If not found + // it means that we have a new launch and new session, and we have to create a + // new set of adapters. + DsfSession session = launch.getSession(); + if (session == null) return null; + + SessionAdapterSet adapter_set; + synchronized(fSessionAdapterSetMap) { + adapter_set = fSessionAdapterSetMap.get(session.getId()); + if (adapter_set == null) { + adapter_set = new SessionAdapterSet(session, launch); + fSessionAdapterSetMap.put(session.getId(), adapter_set); + } + } + + // Returns the adapter type for the launch object. + if (adapterType.equals(IElementLabelProvider.class)) return adapter_set.fViewModelAdapter; + if (adapterType.equals(IElementContentProvider.class)) return adapter_set.fViewModelAdapter; + if (adapterType.equals(IModelProxyFactory.class)) return adapter_set.fViewModelAdapter; + if (adapterType.equals(IColumnPresentationFactory.class)) return adapter_set.fViewModelAdapter; + if (adapterType.equals(ITerminateHandler.class)) return adapter_set.fTerminateCommand; + return null; + } + + @SuppressWarnings("unchecked") + public Class[] getAdapterList() { + return adapter_list; + } + + public void sessionEnded(DsfSession session) { + } + + public void launchesTerminated(ILaunch[] launches) { + } + + public void launchesAdded(ILaunch[] launches) { + } + + public void launchesChanged(ILaunch[] launches) { + } + + public void launchesRemoved(ILaunch[] launches) { + // Dispose the set of adapters for a launch only after the launch is removed. + for (ILaunch launch : launches) { + if (launch instanceof TCFDSFLaunch) { + DsfSession session = ((TCFDSFLaunch)launch).getSession(); + synchronized (fSessionAdapterSetMap) { + if (fSessionAdapterSetMap.containsKey(session.getId())) { + fSessionAdapterSetMap.get(session.getId()).dispose(); + fSessionAdapterSetMap.remove(session); + } + } + } + } + } +} diff --git a/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ContainerLayoutNode.java b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ContainerLayoutNode.java new file mode 100644 index 000000000..31d80eba3 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ContainerLayoutNode.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2006 Ericsson and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ericsson - Initial API and implementation + * Wind River Systems - reused for TCF connection type + *******************************************************************************/ + +package com.windriver.tcf.dsf.ui; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.debug.service.INativeProcesses; +import org.eclipse.dd.dsf.debug.service.IRunControl; +import org.eclipse.dd.dsf.debug.service.INativeProcesses.IProcessDMData; +import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMLayoutNode; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; + +import com.windriver.tcf.dsf.core.services.TCFDSFRunControl; + +@SuppressWarnings("restriction") +public class ContainerLayoutNode extends AbstractDMVMLayoutNode{ + + public ContainerLayoutNode(AbstractVMProvider provider, DsfSession session) { + super(provider, session, IRunControl.IExecutionDMContext.class); + } + + @Override + protected void updateElementsInSessionThread(IChildrenUpdate update) { + if (!checkService(IRunControl.class, null, update)) return; + + IContainerDMContext containerCtx = getServicesTracker().getService(TCFDSFRunControl.class).getContainerDMC(); + update.setChild(new DMVMContext(containerCtx), 0); + update.done(); + } + + @Override + // Labels are only updated for elements that are visible. + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + if (!checkService(IRunControl.class, null, update)) continue; + if (!checkService(INativeProcesses.class, null, update)) continue; + + final IContainerDMContext dmc = findDmcInPath(update.getElementPath(), IContainerDMContext.class); + + INativeProcesses processes = getServicesTracker().getService(INativeProcesses.class); + + String imageKey = null; + + if (getServicesTracker().getService(IRunControl.class).isSuspended(dmc)) { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED; + } else { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_RUNNING; + } + update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0); + + processes.getProcessData( + processes.getProcessForDebugContext(dmc), + new DataRequestMonitor<IProcessDMData>(getSession().getExecutor(), null) { + @SuppressWarnings("restriction") + @Override + public void handleCompleted() { + if (!getStatus().isOK()) { + update.done(); + return; + } + update.setLabel(getData().getName(), 0); + update.done(); + } + }); + } + } +} diff --git a/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/LaunchDialogTabGroup.java b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/LaunchDialogTabGroup.java new file mode 100644 index 000000000..9a7d62dcc --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/LaunchDialogTabGroup.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.ui; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.EnvironmentTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.sourcelookup.SourceLookupTab; + +import com.windriver.debug.tcf.ui.launch.TCFArgumentsTab; +import com.windriver.debug.tcf.ui.launch.TCFMainTab; + +/** + * Launch configuration dialog tab group for TCF over DSF + */ +public class LaunchDialogTabGroup extends AbstractLaunchConfigurationTabGroup { + + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + setTabs(new ILaunchConfigurationTab[] { + new TCFMainTab(), + new TCFArgumentsTab(), + new EnvironmentTab(), + new SourceLookupTab(), + new CommonTab() + }); + } +} diff --git a/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/LaunchVMProvider.java b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/LaunchVMProvider.java new file mode 100644 index 000000000..caf259806 --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/LaunchVMProvider.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.ui; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.dd.dsf.debug.ui.viewmodel.launch.StackFramesLayoutNode; +import org.eclipse.dd.dsf.debug.ui.viewmodel.launch.StandardLaunchRootLayoutNode; +import org.eclipse.dd.dsf.debug.ui.viewmodel.launch.StandardProcessLayoutNode; +import org.eclipse.dd.dsf.debug.ui.viewmodel.launch.StandardLaunchRootLayoutNode.LaunchesEvent; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.dd.dsf.ui.viewmodel.IVMLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.IVMRootLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMProvider; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IDebugEventSetListener; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + + +@SuppressWarnings("restriction") +public class LaunchVMProvider extends AbstractDMVMProvider + implements IDebugEventSetListener, ILaunchesListener2 { + + @ThreadSafe + public LaunchVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, + DsfSession session, ILaunch launch) + { + super(adapter, presentationContext, session); + + IVMRootLayoutNode launchNode = new StandardLaunchRootLayoutNode(this, launch); + // Container node to contain all processes and threads + IVMLayoutNode containerNode = new ContainerLayoutNode(this, getSession()); + IVMLayoutNode processesNode = new StandardProcessLayoutNode(this); + launchNode.setChildNodes(new IVMLayoutNode[] { containerNode, processesNode}); + + IVMLayoutNode threadsNode = new ThreadLayoutNode(this, getSession()); + containerNode.setChildNodes(new IVMLayoutNode[] { threadsNode }); + + IVMLayoutNode stackFramesNode = new StackFramesLayoutNode(this, getSession()); + threadsNode.setChildNodes(new IVMLayoutNode[] { stackFramesNode }); + + setRootLayoutNode(launchNode); + + DebugPlugin.getDefault().addDebugEventListener(this); + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); + } + + + public void handleDebugEvents(final DebugEvent[] events) { + if (isDisposed()) return; + + // We're in session's executor thread. Re-dispach to VM Adapter + // executor thread and then call root layout node. + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + + for (final DebugEvent event : events) { + IVMRootLayoutNode rootLayoutNode = getRootLayoutNode(); + if (rootLayoutNode != null && rootLayoutNode.getDeltaFlags(event) != 0) { + rootLayoutNode.createDelta( + event, + new DataRequestMonitor<IModelDelta>(getExecutor(), null) { + @Override + public void handleCompleted() { + if (getStatus().isOK()) { + getModelProxy().fireModelChangedNonDispatch(getData()); + } + } + @Override + public String toString() { + return "Result of a delta for debug event: '" + event.toString() + + "' in VMP: '" + LaunchVMProvider.this + "'" + + "\n" + getData(); + } + }); + } + } + }}); + } + catch (RejectedExecutionException e) { + // Ignore. This exception could be thrown if the provider is being + // shut down. + } + } + + @Override + public void dispose() { + DebugPlugin.getDefault().removeDebugEventListener(this); + DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this); + super.dispose(); + } + + public void launchesAdded(ILaunch[] launches) { + handleLaunchesEvent(new LaunchesEvent(launches, LaunchesEvent.Type.ADDED)); + } + + public void launchesRemoved(ILaunch[] launches) { + handleLaunchesEvent(new LaunchesEvent(launches, LaunchesEvent.Type.REMOVED)); + } + + public void launchesChanged(ILaunch[] launches) { + handleLaunchesEvent(new LaunchesEvent(launches, LaunchesEvent.Type.CHANGED)); + } + + public void launchesTerminated(ILaunch[] launches) { + handleLaunchesEvent(new LaunchesEvent(launches, LaunchesEvent.Type.TERMINATED)); + } + + private void handleLaunchesEvent(final LaunchesEvent event) { + if (isDisposed()) return; + + // We're in session's executor thread. Re-dispach to VM Adapter + // executor thread and then call root layout node. + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + + IVMRootLayoutNode rootLayoutNode = getRootLayoutNode(); + if (rootLayoutNode != null && rootLayoutNode.getDeltaFlags(event) != 0) { + rootLayoutNode.createDelta( + event, + new DataRequestMonitor<IModelDelta>(getExecutor(), null) { + @Override + public void handleCompleted() { + if (getStatus().isOK()) { + getModelProxy().fireModelChangedNonDispatch(getData()); + } + } + @Override public String toString() { + return "Result of a delta for launch event: '" + event.toString() + + "' in VMP: '" + LaunchVMProvider.this + "'" + + "\n" + getData(); + } + }); + } + }}); + } + catch (RejectedExecutionException e) { + // Ignore. This exception could be thrown if the provider is being + // shut down. + } + } +} diff --git a/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ThreadLayoutNode.java b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ThreadLayoutNode.java new file mode 100644 index 000000000..54ef832fc --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ThreadLayoutNode.java @@ -0,0 +1,197 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for multi threaded functionality + *******************************************************************************/ +package com.windriver.tcf.dsf.ui; + +import java.util.List; +import java.util.Map; + +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.dsf.datamodel.IDMEvent; +import org.eclipse.dd.dsf.debug.service.INativeProcesses; +import org.eclipse.dd.dsf.debug.service.IRunControl; +import org.eclipse.dd.dsf.debug.service.INativeProcesses.IThreadDMData; +import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMData; +import org.eclipse.dd.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.dd.dsf.debug.service.IRunControl.IStartedDMEvent; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.dd.dsf.ui.viewmodel.IVMContext; +import org.eclipse.dd.dsf.ui.viewmodel.IVMLayoutNode; +import org.eclipse.dd.dsf.ui.viewmodel.VMDelta; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMLayoutNode; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; + +import com.windriver.tcf.dsf.core.services.TCFDSFExecutionDMC; + +@SuppressWarnings("restriction") +public class ThreadLayoutNode extends AbstractDMVMLayoutNode { + + public ThreadLayoutNode(AbstractVMProvider provider, DsfSession session) { + super(provider, session, IRunControl.IExecutionDMContext.class); + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + if (!checkService(IRunControl.class, null, update)) return; + final IContainerDMContext contDmc = findDmcInPath(update.getElementPath(), IContainerDMContext.class); + + if (contDmc == null) { + handleFailedUpdate(update); + return; + } + + getServicesTracker().getService(IRunControl.class).getExecutionContexts(contDmc, + new DataRequestMonitor<IExecutionDMContext[]>(getSession().getExecutor(), null){ + @Override + public void handleCompleted() { + if (!getStatus().isOK()) { + handleFailedUpdate(update); + return; + } + fillUpdateWithVMCs(update, getData()); + update.done(); + } + }); + } + + @Override + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + if (!checkService(IRunControl.class, null, update)) continue; + if (!checkService(INativeProcesses.class, null, update)) continue; + + final IExecutionDMContext dmc = findDmcInPath(update.getElementPath(), IExecutionDMContext.class); + + INativeProcesses processes = getServicesTracker().getService(INativeProcesses.class); + + String imageKey = null; + if (getServicesTracker().getService(IRunControl.class).isSuspended(dmc)) { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED; + } + else { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_RUNNING; + } + update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0); + + // Find the Reason for the State + final StringBuilder reason = new StringBuilder(); + getServicesTracker().getService(IRunControl.class).getExecutionData(dmc, + new DataRequestMonitor<IExecutionDMData>(getSession().getExecutor(), null) { + @Override + public void handleCompleted(){ + if (!getStatus().isOK()) { + update.done(); + return; + } + if(getData().getStateChangeReason() != null){ + reason.append(": " + getData().getStateChangeReason() ); //$NON-NLS-1$ + } + } + }); + + getServicesTracker().getService(INativeProcesses.class).getThreadData( + processes.getThreadForDebugContext(dmc), + new DataRequestMonitor<IThreadDMData>(getSession().getExecutor(), null) { + @Override + public void handleCompleted() { + if (!getStatus().isOK()) { + update.done(); + return; + } + final StringBuilder builder = new StringBuilder("Thread["); //$NON-NLS-1$ + builder.append(((TCFDSFExecutionDMC)dmc).getTcfContextId()); + builder.append("] "); //$NON-NLS-1$ + builder.append(getData().getId()); + builder.append(getData().getName()); + if(getServicesTracker().getService(IRunControl.class).isSuspended(dmc)) + builder.append(" (Suspended"); //$NON-NLS-1$ + else + builder.append(" (Running"); //$NON-NLS-1$ + // Reason will be null before ContainerSuspendEvent is fired + if(reason.length() > 0 ) + builder.append(reason); + builder.append(")"); //$NON-NLS-1$ + update.setLabel(builder.toString(), 0); + update.done(); + } + }); + } + } + + @Override + protected int getNodeDeltaFlagsForDMEvent(IDMEvent<?> e) { + if(e instanceof IRunControl.IContainerResumedDMEvent || e instanceof IRunControl.IContainerSuspendedDMEvent || e instanceof IStartedDMEvent || e instanceof IExitedDMEvent) { + return IModelDelta.CONTENT; + } + if(e instanceof IRunControl.IResumedDMEvent || e instanceof IRunControl.ISuspendedDMEvent) { + return IModelDelta.STATE; + } + return IModelDelta.NO_CHANGE; + } + + @Override + protected void buildDeltaForDMEvent(final IDMEvent<?> e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + if(e instanceof IRunControl.IContainerResumedDMEvent || e instanceof IRunControl.IContainerSuspendedDMEvent) { + // Since IContainerDMContext sub-classes IExecutionDMContext, container + // events require special processing: + // Retrieve all the thread elements and mark their state as changed. + // Then pass these elements to the child layout nodes for processing + final Map<IVMLayoutNode,Integer> childNodeDeltas = getChildNodesWithDeltaFlags(e); + if (childNodeDeltas.size() == 0) { + // There are no child nodes with deltas, just return to parent. + requestMonitor.done(); + return; + } + + // Calculate the index of this node by retrieving all the + // elements and then finding the DMC that the event is for. + updateElements(new ElementsUpdate( + new DataRequestMonitor<List<Object>>(getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isDisposed()) return; + + // Check for an empty list of elements. If it's empty then we + // don't have to call the children nodes, so return here. + // No need to propagate error, there's no means or need to display it. + if (!getStatus().isOK() || getData().isEmpty()) { + requestMonitor.done(); + return; + } + + for (int i = 0; i < getData().size(); i++) { + IVMContext vmc = (IVMContext)getData().get(i); + VMDelta delta = parentDelta.addNode(vmc, nodeOffset + i, IModelDelta.STATE); + callChildNodesToBuildDelta(childNodeDeltas, delta, e, requestMonitor); + if (vmc.equals(getData().get(i))) break; + } + } + }, + parentDelta)); + return; + } + else if (e instanceof IRunControl.IResumedDMEvent || e instanceof IRunControl.ISuspendedDMEvent) { + parentDelta.addNode(new DMVMContext(e.getDMContext()), IModelDelta.STATE); + super.buildDeltaForDMEvent(e, parentDelta, nodeOffset, requestMonitor); + } + else { + super.buildDeltaForDMEvent(e, parentDelta, nodeOffset, requestMonitor); + } + } +} diff --git a/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ViewModelAdapter.java b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ViewModelAdapter.java new file mode 100644 index 000000000..e70d2704c --- /dev/null +++ b/plugins/com.windriver.tcf.dsf.ui/src/com/windriver/tcf/dsf/ui/ViewModelAdapter.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.dsf.ui; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.dd.dsf.concurrent.ThreadSafe; +import org.eclipse.dd.dsf.debug.ui.viewmodel.expression.ExpressionVMProvider; +import org.eclipse.dd.dsf.debug.ui.viewmodel.register.RegisterVMProvider; +import org.eclipse.dd.dsf.debug.ui.viewmodel.variable.VariableVMProvider; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMAdapter; +import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.osgi.framework.Bundle; + +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.dsf.core.launch.TCFDSFLaunch; + +@ThreadSafe +@SuppressWarnings("restriction") +public class ViewModelAdapter extends AbstractDMVMAdapter implements IElementLabelProvider { + + private final TCFDSFLaunch launch; + + public ViewModelAdapter(DsfSession session, TCFDSFLaunch launch) { + super(session); + this.launch = launch; + getSession().registerModelAdapter(IColumnPresentationFactory.class, this); + } + + @Override + public void dispose() { + getSession().unregisterModelAdapter(IColumnPresentationFactory.class); + super.dispose(); + } + + @Override + protected AbstractDMVMProvider createViewModelProvider(IPresentationContext context) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId()) ) { + return new LaunchVMProvider(this, context, getSession(), launch); + } + if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(context.getId()) ) { + return new VariableVMProvider(this, context, getSession()); + } + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(context.getId()) ) { + return new RegisterVMProvider(this, context, getSession()); + } + if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(context.getId()) ) { + return new ExpressionVMProvider(this, context, getSession()); + } + return null; + } + + private static final Map<String,ImageDescriptor> image_cache = + new HashMap<String,ImageDescriptor>(); + + private static ImageDescriptor getImageDescriptor(String name) { + if (name == null) return null; + ImageDescriptor descriptor = image_cache.get(name); + if (descriptor == null) { + descriptor = ImageDescriptor.getMissingImageDescriptor(); + Bundle bundle = Platform.getBundle("org.eclipse.debug.ui"); + if (bundle != null){ + URL url = FileLocator.find(bundle, new Path(name), null); + descriptor = ImageDescriptor.createFromURL(url); + } + image_cache.put(name, descriptor); + } + return descriptor; + } + + public void update(final ILabelUpdate[] updates) { + Protocol.invokeLater(new Runnable() { + public void run() { + for (ILabelUpdate u : updates) { + Object o = u.getElement(); + if (o instanceof TCFDSFLaunch) { + u.setImageDescriptor(getImageDescriptor("icons/full/obj16/ldebug_obj.gif"), 0); + TCFDSFLaunch launch = (TCFDSFLaunch)o; + String status = ""; + if (launch.isConnecting()) status = "Connecting"; + else if (launch.isDisconnected()) status = "Disconnected"; + else if (launch.isTerminated()) status = "Terminated"; + Throwable error = launch.getError(); + if (error != null) { + status += " - " + error; + u.setForeground(new RGB(255, 0, 0), 0); + } + if (status.length() > 0) status = " (" + status + ")"; + u.setLabel(launch.getLaunchConfiguration().getName() + status, 0); + } + else { + u.setForeground(new RGB(255, 0, 0), 0); + u.setLabel("Invalid object: " + o.getClass(), 0); + } + } + Display.getDefault().asyncExec(new Runnable() { + public void run() { + for (ILabelUpdate u : updates) u.done(); + } + }); + } + }); + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/.classpath b/plugins/com.windriver.tcf.rse.ui/.classpath new file mode 100644 index 000000000..751c8f2e5 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/com.windriver.tcf.rse.ui/.project b/plugins/com.windriver.tcf.rse.ui/.project new file mode 100644 index 000000000..bcca78522 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.windriver.tcf.rse.ui</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/plugins/com.windriver.tcf.rse.ui/META-INF/MANIFEST.MF b/plugins/com.windriver.tcf.rse.ui/META-INF/MANIFEST.MF new file mode 100644 index 000000000..b45ab5134 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: com.windriver.tcf.rse.ui;singleton:=true +Bundle-Version: 0.1.0 +Bundle-Activator: com.windriver.tcf.rse.ui.Activator +Bundle-Vendor: %providerName +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.rse.core, + org.eclipse.rse.ui, + org.eclipse.rse.services, + com.windriver.tcf.api, + org.eclipse.ui.views, + org.eclipse.rse.subsystems.files.core, + org.eclipse.rse.subsystems.processes.core +Eclipse-LazyStart: true +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/plugins/com.windriver.tcf.rse.ui/about.html b/plugins/com.windriver.tcf.rse.ui/about.html new file mode 100755 index 000000000..6c5b3615b --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>January 10, 2008</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/plugins/com.windriver.tcf.rse.ui/build.properties b/plugins/com.windriver.tcf.rse.ui/build.properties new file mode 100644 index 000000000..e9863e281 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/plugins/com.windriver.tcf.rse.ui/icons/process-r.gif b/plugins/com.windriver.tcf.rse.ui/icons/process-r.gif Binary files differnew file mode 100644 index 000000000..87aa144a8 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/process-r.gif diff --git a/plugins/com.windriver.tcf.rse.ui/icons/process-s.gif b/plugins/com.windriver.tcf.rse.ui/icons/process-s.gif Binary files differnew file mode 100644 index 000000000..c1644b2ec --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/process-s.gif diff --git a/plugins/com.windriver.tcf.rse.ui/icons/system-files-live.gif b/plugins/com.windriver.tcf.rse.ui/icons/system-files-live.gif Binary files differnew file mode 100644 index 000000000..885b8a69e --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/system-files-live.gif diff --git a/plugins/com.windriver.tcf.rse.ui/icons/system-files.gif b/plugins/com.windriver.tcf.rse.ui/icons/system-files.gif Binary files differnew file mode 100644 index 000000000..874c99262 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/system-files.gif diff --git a/plugins/com.windriver.tcf.rse.ui/icons/system-processes-live.gif b/plugins/com.windriver.tcf.rse.ui/icons/system-processes-live.gif Binary files differnew file mode 100644 index 000000000..96a447a71 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/system-processes-live.gif diff --git a/plugins/com.windriver.tcf.rse.ui/icons/system-processes.gif b/plugins/com.windriver.tcf.rse.ui/icons/system-processes.gif Binary files differnew file mode 100644 index 000000000..e07b81aa6 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/system-processes.gif diff --git a/plugins/com.windriver.tcf.rse.ui/icons/tcf.gif b/plugins/com.windriver.tcf.rse.ui/icons/tcf.gif Binary files differnew file mode 100644 index 000000000..3198679ae --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/tcf.gif diff --git a/plugins/com.windriver.tcf.rse.ui/icons/thread-r.gif b/plugins/com.windriver.tcf.rse.ui/icons/thread-r.gif Binary files differnew file mode 100644 index 000000000..17d7d69df --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/thread-r.gif diff --git a/plugins/com.windriver.tcf.rse.ui/icons/thread-s.gif b/plugins/com.windriver.tcf.rse.ui/icons/thread-s.gif Binary files differnew file mode 100644 index 000000000..3fa5fe586 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/icons/thread-s.gif diff --git a/plugins/com.windriver.tcf.rse.ui/plugin.properties b/plugins/com.windriver.tcf.rse.ui/plugin.properties new file mode 100644 index 000000000..f736bbb13 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2007 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Wind River Systems - initial implementation +############################################################################### +pluginName = TCF/RSE Integration UI +providerName = Eclipse.org + diff --git a/plugins/com.windriver.tcf.rse.ui/plugin.xml b/plugins/com.windriver.tcf.rse.ui/plugin.xml new file mode 100644 index 000000000..def23b5df --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/plugin.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + <extension + point="org.eclipse.rse.core.systemTypes"> + <systemType + description="Connects using Target Commenucation Framework" + icon="icons/tcf.gif" + iconLive="icons/tcf.gif" + id="com.windriver.tcf.rse.ui.systemType" + label="TCF" + name="TCF"> + </systemType> + </extension> + <extension + point="org.eclipse.rse.core.subsystemConfigurations"> + <configuration + category="processes" + class="com.windriver.tcf.rse.ui.TCFProcessSubSystemConfiguration" + description="Processes" + icon="icons/system-processes.gif" + iconlive="icons/system-processes-live.gif" + id="com.windriver.tcf.rse.ui.Processes" + name="Processes" + priority="200" + systemTypeIds="com.windriver.tcf.rse.ui.systemType" + vendor="Wind River"> + </configuration> + </extension> + <extension + point="org.eclipse.rse.core.subsystemConfigurations"> + <configuration + category="files" + class="com.windriver.tcf.rse.ui.TCFFileSubSystemConfiguration" + description="Files" + icon="icons/system-files.gif" + iconlive="icons/system-files-live.gif" + id="com.windriver.tcf.rse.ui.Files" + name="Files" + priority="200" + systemTypeIds="com.windriver.tcf.rse.ui.systemType" + vendor="Wind River"> + </configuration> + </extension> + <extension point="org.eclipse.core.runtime.adapters"> + <factory + class="com.windriver.tcf.rse.ui.TCFSystemViewProcessAdapterFactory" + adaptableType="com.windriver.tcf.rse.ui.TCFRemoteProcess"> + <adapter type="org.eclipse.rse.ui.view.ISystemViewElementAdapter"/> + <adapter type="org.eclipse.rse.ui.view.ISystemDragDropAdapter"/> + <adapter type="org.eclipse.rse.ui.view.ISystemRemoteElementAdapter"/> + <adapter type="org.eclipse.ui.views.properties.IPropertySource"/> + <adapter type="org.eclipse.ui.model.IWorkbenchAdapter"/> + <adapter type="org.eclipse.ui.IActionFilter"/> + <adapter type="org.eclipse.ui.progress.IDeferredWorkbenchAdapter"/> + <adapter type="org.eclipse.rse.core.subsystems.IRemoteObjectIdentifier"/> + </factory> + </extension> +</plugin> diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/Activator.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/Activator.java new file mode 100644 index 000000000..682b8d4c2 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/Activator.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.windriver.tcf.rse.ui"; + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + public ImageDescriptor getImageDescriptorFromPath(String path) { + return AbstractUIPlugin.imageDescriptorFromPlugin(PLUGIN_ID, path); + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/ITCFFileService.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/ITCFFileService.java new file mode 100644 index 000000000..c0a981cb6 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/ITCFFileService.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import com.windriver.tcf.api.services.IFileSystem; + +public interface ITCFFileService { + + public boolean canRead(IFileSystem.FileAttrs attrs); + + public boolean canWrite(IFileSystem.FileAttrs attrs); + +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/ITCFSubSystem.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/ITCFSubSystem.java new file mode 100644 index 000000000..c842c6f8c --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/ITCFSubSystem.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.rse.core.subsystems.ISubSystem; + +/** + * Subsystem can implement this interface to indicate that it can share TCF connection with + * other subsystems on same host. + */ +public interface ITCFSubSystem extends ISubSystem { + +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/Messages.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/Messages.java new file mode 100644 index 000000000..57d898ebf --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/Messages.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "com.windriver.tcf.rse.ui.messages"; //$NON-NLS-1$ + + public static String SysMonitor_AllProcesses; + public static String SysMonitor_Process; + + // PROCESS PROPERTIES + public static String PROCESS_ID_LABEL; + public static String PROCESS_PID_LABEL; + public static String PROCESS_NAME_LABEL; + public static String PROCESS_CWD_LABEL; + public static String PROCESS_ROOT_LABEL; + public static String PROCESS_UID_LABEL; + public static String PROCESS_USERNAME_LABEL; + public static String PROCESS_GID_LABEL; + public static String PROCESS_GROUPNAME_LABEL; + public static String PROCESS_PPID_LABEL; + public static String PROCESS_PGRP_LABEL; + public static String PROCESS_STATE_LABEL; + public static String PROCESS_TRACERPID_LABEL; + public static String PROCESS_VMSIZE_LABEL; + public static String PROCESS_VMRSS_LABEL; + public static String PROCESS_SESSION_LABEL; + public static String PROCESS_TTY_LABEL; + public static String PROCESS_TGID_LABEL; + public static String PROCESS_FLAGS_LABEL; + public static String PROCESS_MINFLT_LABEL; + public static String PROCESS_CMINFLT_LABEL; + public static String PROCESS_MAJFLT_LABEL; + public static String PROCESS_CMAJFLT_LABEL; + public static String PROCESS_UTIME_LABEL; + public static String PROCESS_STIME_LABEL; + public static String PROCESS_CUTIME_LABEL; + public static String PROCESS_CSTIME_LABEL; + public static String PROCESS_PC_UTIME_LABEL; + public static String PROCESS_PC_STIME_LABEL; + public static String PROCESS_PRIORITY_LABEL; + public static String PROCESS_NICE_LABEL; + public static String PROCESS_ITREALVALUE_LABEL; + public static String PROCESS_STARTTIME_LABEL; + public static String PROCESS_RLIMIT_LABEL; + public static String PROCESS_CODESTART_LABEL; + public static String PROCESS_CODEEND_LABEL; + public static String PROCESS_STACKSTART_LABEL; + public static String PROCESS_SIGNALS_LABEL; + public static String PROCESS_SIGBLOCK_LABEL; + public static String PROCESS_SIGIGNORE_LABEL; + public static String PROCESS_SIGCATCH_LABEL; + public static String PROCESS_WCHAN_LABEL; + public static String PROCESS_NSWAP_LABEL; + public static String PROCESS_CNSWAP_LABEL; + public static String PROCESS_EXITSIGNAL_LABEL; + public static String PROCESS_PROCESSOR_LABEL; + public static String PROCESS_RTPRIORITY_LABEL; + public static String PROCESS_POLICY_LABEL; + + public static String PROCESS_ID_TOOLTIP; + public static String PROCESS_PID_TOOLTIP; + public static String PROCESS_NAME_TOOLTIP; + public static String PROCESS_CWD_TOOLTIP; + public static String PROCESS_ROOT_TOOLTIP; + public static String PROCESS_UID_TOOLTIP; + public static String PROCESS_USERNAME_TOOLTIP; + public static String PROCESS_GID_TOOLTIP; + public static String PROCESS_GROUPNAME_TOOLTIP; + public static String PROCESS_PPID_TOOLTIP; + public static String PROCESS_PGRP_TOOLTIP; + public static String PROCESS_STATE_TOOLTIP; + public static String PROCESS_TRACERPID_TOOLTIP; + public static String PROCESS_VMSIZE_TOOLTIP; + public static String PROCESS_VMRSS_TOOLTIP; + public static String PROCESS_SESSION_TOOLTIP; + public static String PROCESS_TTY_TOOLTIP; + public static String PROCESS_TGID_TOOLTIP; + public static String PROCESS_FLAGS_TOOLTIP; + public static String PROCESS_MINFLT_TOOLTIP; + public static String PROCESS_CMINFLT_TOOLTIP; + public static String PROCESS_MAJFLT_TOOLTIP; + public static String PROCESS_CMAJFLT_TOOLTIP; + public static String PROCESS_UTIME_TOOLTIP; + public static String PROCESS_STIME_TOOLTIP; + public static String PROCESS_PC_UTIME_TOOLTIP; + public static String PROCESS_PC_STIME_TOOLTIP; + public static String PROCESS_CUTIME_TOOLTIP; + public static String PROCESS_CSTIME_TOOLTIP; + public static String PROCESS_PRIORITY_TOOLTIP; + public static String PROCESS_NICE_TOOLTIP; + public static String PROCESS_ITREALVALUE_TOOLTIP; + public static String PROCESS_STARTTIME_TOOLTIP; + public static String PROCESS_RLIMIT_TOOLTIP; + public static String PROCESS_CODESTART_TOOLTIP; + public static String PROCESS_CODEEND_TOOLTIP; + public static String PROCESS_STACKSTART_TOOLTIP; + public static String PROCESS_SIGNALS_TOOLTIP; + public static String PROCESS_SIGBLOCK_TOOLTIP; + public static String PROCESS_SIGIGNORE_TOOLTIP; + public static String PROCESS_SIGCATCH_TOOLTIP; + public static String PROCESS_WCHAN_TOOLTIP; + public static String PROCESS_NSWAP_TOOLTIP; + public static String PROCESS_CNSWAP_TOOLTIP; + public static String PROCESS_EXITSIGNAL_TOOLTIP; + public static String PROCESS_PROCESSOR_TOOLTIP; + public static String PROCESS_RTPRIORITY_TOOLTIP; + public static String PROCESS_POLICY_TOOLTIP; + + public static String PROCESS_VMSIZE_VALUE; + public static String PROCESS_VMRSS_VALUE; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFConnectorService.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFConnectorService.java new file mode 100644 index 000000000..ebfa0031d --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFConnectorService.java @@ -0,0 +1,275 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.core.subsystems.AbstractConnectorService; +import org.eclipse.rse.core.subsystems.CommunicationsEvent; + +import com.windriver.tcf.api.core.AbstractPeer; +import com.windriver.tcf.api.core.ChannelTCP; +import com.windriver.tcf.api.protocol.IChannel; +import com.windriver.tcf.api.protocol.IPeer; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IFileSystem; +import com.windriver.tcf.api.services.ILocator; +import com.windriver.tcf.api.services.ISysMonitor; + +class TCFConnectorService extends AbstractConnectorService { + + private IChannel channel; + private Throwable channel_error; + private final List<Runnable> state_change = new ArrayList<Runnable>(); + + public TCFConnectorService(IHost host, int port) { + super("TCF", "Target Communication Framework", host, port); + } + + @Override + protected void internalConnect(IProgressMonitor monitor) throws Exception { + assert !Protocol.isDispatchThread(); + final Exception[] res = new Exception[1]; + monitor.beginTask("Connecting " + getHostName(), 1); + synchronized (res) { + Protocol.invokeLater(new Runnable() { + public void run() { + connectTCFChannel(res); + } + }); + res.wait(); + } + monitor.done(); + if (res[0] != null) throw res[0]; + } + + @Override + protected void internalDisconnect(IProgressMonitor monitor) throws Exception { + assert !Protocol.isDispatchThread(); + final Exception[] res = new Exception[1]; + monitor.beginTask("Disconnecting " + getHostName(), 1); + synchronized (res) { + Protocol.invokeLater(new Runnable() { + public void run() { + disconnectTCFChannel(res); + } + }); + res.wait(); + } + monitor.done(); + if (res[0] != null) throw res[0]; + } + + public void acquireCredentials(boolean refresh) throws InterruptedException { + } + + public void clearCredentials() { + } + + public void clearPassword(boolean persist, boolean propagate) { + } + + public String getUserId() { + return null; + } + + public boolean hasPassword(boolean persistent) { + return false; + } + + public boolean inheritsCredentials() { + return false; + } + + public boolean isConnected() { + final boolean res[] = new boolean[1]; + Protocol.invokeAndWait(new Runnable() { + public void run() { + res[0] = channel != null && channel.getState() == IChannel.STATE_OPEN; + } + }); + return res[0]; + } + + public boolean isSuppressed() { + return false; + } + + public void removePassword() { + } + + public void removeUserId() { + } + + public boolean requiresPassword() { + return false; + } + + public boolean requiresUserId() { + return false; + } + + public void savePassword() { + } + + public void saveUserId() { + } + + public void setPassword(String matchingUserId, String password, + boolean persist, boolean propagate) { + } + + public void setSuppressed(boolean suppress) { + } + + public void setUserId(String userId) { + } + + public boolean sharesCredentials() { + return false; + } + + public boolean supportsPassword() { + return false; + } + + public boolean supportsUserId() { + return false; + } + + private void connectTCFChannel(final Exception[] res) { + if (channel != null) { + switch (channel.getState()) { + case IChannel.STATE_OPEN: + case IChannel.STATE_CLOSED: + synchronized (res) { + if (channel_error instanceof Exception) res[0] = (Exception)channel_error; + else if (channel_error != null) res[0] = new Exception(channel_error); + else res[0] = null; + res.notify(); + } + return; + } + } + if (channel == null) { + final String host = getHostName().toLowerCase(); + // TODO: final int port = getPort(); + final int port = TCFConnectorServiceManager.TCF_PORT; + IPeer peer = null; + String ports = Integer.toString(port); + ILocator locator = Protocol.getLocator(); + for (Iterator<IPeer> i = locator.getPeers().values().iterator(); i.hasNext();) { + IPeer p = i.next(); + Map<String, String> attrs = p.getAttributes(); + if ("TCP".equals(attrs.get(IPeer.ATTR_TRANSPORT_NAME)) && + host.equalsIgnoreCase(attrs.get(IPeer.ATTR_IP_HOST)) && + ports.equals(attrs.get(IPeer.ATTR_IP_PORT))) { + peer = p; + break; + } + } + if (peer == null) { + Map<String, String> attrs = new HashMap<String, String>(); + attrs.put(IPeer.ATTR_ID, "RSE:" + host + ":" + port); + attrs.put(IPeer.ATTR_NAME, getName()); + attrs.put(IPeer.ATTR_TRANSPORT_NAME, "TCP"); + attrs.put(IPeer.ATTR_IP_HOST, host); + attrs.put(IPeer.ATTR_IP_PORT, ports); + peer = new AbstractPeer(attrs) { + public IChannel openChannel() { + return new ChannelTCP(this, host, port); + } + }; + } + channel = peer.openChannel(); + channel.addChannelListener(new IChannel.IChannelListener() { + + public void onChannelOpened() { + onConnected(); + } + + public void congestionLevel(int level) { + } + + public void onChannelClosed(Throwable error) { + channel.removeChannelListener(this); + onDisconnected(error); + } + + }); + assert channel.getState() == IChannel.STATE_OPENNING; + } + state_change.add(new Runnable() { + public void run() { + connectTCFChannel(res); + } + }); + } + + private void disconnectTCFChannel(final Exception[] res) { + if (channel == null || channel.getState() == IChannel.STATE_CLOSED) { + synchronized (res) { + res[0] = null; + res.notify(); + } + return; + } + if (channel.getState() == IChannel.STATE_OPEN) channel.close(); + state_change.add(new Runnable() { + public void run() { + disconnectTCFChannel(res); + } + }); + } + + private void onConnected() { + assert channel != null; + if (state_change.isEmpty()) return; + Runnable[] r = state_change.toArray(new Runnable[state_change.size()]); + state_change.clear(); + for (int i = 0; i < r.length; i++) r[i].run(); + } + + private void onDisconnected(Throwable error) { + assert channel != null; + channel_error = error; + if (state_change.isEmpty()) { + fireCommunicationsEvent(CommunicationsEvent.CONNECTION_ERROR); + } + else { + Runnable[] r = state_change.toArray(new Runnable[state_change.size()]); + state_change.clear(); + for (int i = 0; i < r.length; i++) r[i].run(); + } + channel = null; + channel_error = null; + } + + public ISysMonitor getSysMonitorService() { + if (channel == null || channel.getState() != IChannel.STATE_OPEN) throw new Error("Not connected"); + ISysMonitor m = channel.getRemoteService(ISysMonitor.class); + if (m == null) throw new Error("Remote peer does not support SysMonitor service"); + return m; + } + + public IFileSystem getFileSystemService() { + if (channel == null || channel.getState() != IChannel.STATE_OPEN) throw new Error("Not connected"); + IFileSystem m = channel.getRemoteService(IFileSystem.class); + if (m == null) throw new Error("Remote peer does not support FileSystem service"); + return m; + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFConnectorServiceManager.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFConnectorServiceManager.java new file mode 100644 index 000000000..e13fd58d8 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFConnectorServiceManager.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.core.subsystems.AbstractConnectorServiceManager; +import org.eclipse.rse.core.subsystems.IConnectorService; +import org.eclipse.rse.core.subsystems.ISubSystem; + +public class TCFConnectorServiceManager extends AbstractConnectorServiceManager { + + public static int TCF_PORT = 1534; + + private static final TCFConnectorServiceManager manager = + new TCFConnectorServiceManager(); + + @Override + public IConnectorService createConnectorService(IHost host) { + return new TCFConnectorService(host, TCF_PORT); + } + + @SuppressWarnings("unchecked") + @Override + public Class getSubSystemCommonInterface(ISubSystem subsystem) { + return ITCFSubSystem.class; + } + + @Override + public boolean sharesSystem(ISubSystem otherSubSystem) { + return otherSubSystem instanceof ITCFSubSystem; + } + + public static TCFConnectorServiceManager getInstance() { + return manager; + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileAdapter.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileAdapter.java new file mode 100644 index 000000000..e5164df66 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileAdapter.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.rse.services.files.IHostFile; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.FileServiceSubSystem; +import org.eclipse.rse.subsystems.files.core.subsystems.IHostFileToRemoteFileAdapter; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileContext; + +public class TCFFileAdapter implements IHostFileToRemoteFileAdapter { + + public IRemoteFile convertToRemoteFile(FileServiceSubSystem ss, + IRemoteFileContext ctx, IRemoteFile parent, IHostFile node) { + return new TCFRemoteFile(ss, ctx, parent, node); + } + + public IRemoteFile[] convertToRemoteFiles(FileServiceSubSystem ss, + IRemoteFileContext ctx, IRemoteFile parent, IHostFile[] nodes) { + if (nodes == null) return null; + IRemoteFile[] res = new IRemoteFile[nodes.length]; + for (int i = 0; i < res.length; i++) { + res[i] = new TCFRemoteFile(ss, ctx, parent, nodes[i]); + } + return res; + } + +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileResource.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileResource.java new file mode 100644 index 000000000..39cfa52e6 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileResource.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.rse.core.subsystems.AbstractResource; +import org.eclipse.rse.services.files.IHostFile; + +import com.windriver.tcf.api.services.IFileSystem; + +public class TCFFileResource extends AbstractResource implements IHostFile { + + private final ITCFFileService service; + private String parent; + private String name; + private final IFileSystem.FileAttrs attrs; + private final boolean root; + + public TCFFileResource(ITCFFileService service, String parent, String name, + IFileSystem.FileAttrs attrs, boolean root) { + if (name == null) { + int i = parent.lastIndexOf('/'); + if (i > 0) { + name = parent.substring(i + 1); + parent = parent.substring(0, i); + } + } + this.service = service; + this.parent = parent; + this.name = name; + this.attrs = attrs; + this.root = root; + } + + private String toLocalPath(String path) { + if (path.length() > 1 && path.charAt(1) == ':') { + return path.replace('/', '\\'); + } + else { + return path.replace('\\', '/'); + } + } + + public boolean canRead() { + return attrs != null && service.canRead(attrs); + } + + public boolean canWrite() { + return attrs != null && service.canWrite(attrs); + } + + public boolean exists() { + return attrs != null; + } + + public synchronized String getAbsolutePath() { + if (root) return toLocalPath(name); + if (parent.endsWith("/")) return toLocalPath(parent + name); + return toLocalPath(parent + '/' + name); + } + + public long getModifiedDate() { + if (attrs == null) return 0; + if ((attrs.flags & IFileSystem.ATTR_ACMODTIME) == 0) return 0; + return attrs.mtime; + } + + public synchronized String getName() { + return toLocalPath(name); + } + + public synchronized String getParentPath() { + return toLocalPath(parent); + } + + public long getSize() { + if (attrs == null) return 0; + if ((attrs.flags & IFileSystem.ATTR_SIZE) == 0) return 0; + return attrs.size; + } + + public boolean isArchive() { + return false; + } + + public boolean isDirectory() { + if (attrs == null) return false; + return attrs.isDirectory(); + } + + public boolean isFile() { + if (attrs == null) return false; + return attrs.isFile(); + } + + public synchronized boolean isHidden() { + return name.startsWith("."); + } + + public synchronized boolean isRoot() { + return root; + } + + public synchronized void renameTo(String path) { + path = path.replace('\\', '/'); + if (path.equals("/")) { + parent = name = "/"; + return; + } + assert !path.endsWith("/"); + int i = path.lastIndexOf('/'); + parent = path.substring(0, i); + name = path.substring(i + 1); + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileService.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileService.java new file mode 100644 index 000000000..6eb030f61 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileService.java @@ -0,0 +1,643 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.services.clientserver.FileTypeMatcher; +import org.eclipse.rse.services.clientserver.IMatcher; +import org.eclipse.rse.services.clientserver.NamePatternMatcher; +import org.eclipse.rse.services.clientserver.messages.IndicatorException; +import org.eclipse.rse.services.clientserver.messages.SystemMessage; +import org.eclipse.rse.services.clientserver.messages.SystemMessageException; +import org.eclipse.rse.services.files.AbstractFileService; +import org.eclipse.rse.services.files.IHostFile; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.IFileSystem; +import com.windriver.tcf.api.services.IFileSystem.DirEntry; +import com.windriver.tcf.api.services.IFileSystem.FileAttrs; +import com.windriver.tcf.api.services.IFileSystem.FileSystemException; +import com.windriver.tcf.api.services.IFileSystem.IFileHandle; +import com.windriver.tcf.api.util.TCFFileInputStream; +import com.windriver.tcf.api.util.TCFFileOutputStream; + +public class TCFFileService extends AbstractFileService implements ITCFFileService { + + private final TCFConnectorService connector; + + private UserInfo user_info; + + private static final class UserInfo { + final int r_uid; + final int e_uid; + final int r_gid; + final int e_gid; + final String home; + + final Throwable error; + + UserInfo(int r_uid, int e_uid, int r_gid, int e_gid, String home) { + this.r_uid = r_uid; + this.e_uid = e_uid; + this.r_gid = r_gid; + this.e_gid = e_gid; + this.home = home; + error = null; + } + + UserInfo(Throwable error) { + this.error = error; + r_uid = -1; + e_uid = -1; + r_gid = -1; + e_gid = -1; + home = null; + } + } + + public TCFFileService(IHost host) { + connector = (TCFConnectorService)TCFConnectorServiceManager + .getInstance().getConnectorService(host, ITCFSubSystem.class); + } + + public String getDescription() { + return "The TCF File Service uses the Target Communication Framework to provide service" + + "for the Files subsystem. It requires a TCF agent to be running on the remote machine."; + } + + public SystemMessage getMessage(String id) { + try { + return new SystemMessage("TCF", "C", "0001", + SystemMessage.ERROR, id, ""); + } + catch (IndicatorException e) { + throw new Error(e); + } + } + + public SystemMessage getMessage(Throwable x) { + try { + return new SystemMessage("TCF", "C", "0002", + SystemMessage.ERROR, x.getClass().getName(), x.getMessage()); + } + catch (IndicatorException e) { + throw new Error(e); + } + } + + public String getName() { + return "TCF File Service"; + } + + public void initService(IProgressMonitor monitor) { + } + + public void uninitService(IProgressMonitor monitor) { + } + + private String toRemotePath(String parent, String name) throws SystemMessageException { + assert !Protocol.isDispatchThread(); + String s = null; + if (parent != null) parent = parent.replace('\\', '/'); + if (name != null) name = name.replace('\\', '/'); + if (parent == null || parent.length() == 0) s = name; + else if (name == null || name.equals(".")) s = parent; + else if (name.equals("/")) s = parent; + else if (parent.endsWith("/")) s = parent + name; + else s = parent + '/' + name; + if (s.startsWith("./") || s.equals(".")) { + UserInfo ui = getUserInfo(); + if (ui.error != null) throw new SystemMessageException(getMessage(ui.error)); + s = ui.home.replace('\\', '/') + s.substring(1); + } + while (s.endsWith("/.")) s = s.substring(0, s.length() - 2); + return s; + } + + public boolean copy(String srcParent, + String srcName, String tgtParent, String tgtName, IProgressMonitor monitor) + throws SystemMessageException { + final String src = toRemotePath(srcParent, srcName); + final String tgt = toRemotePath(tgtParent, tgtName); + return new TCFRSETask<Boolean>() { + public void run() { + IFileSystem fs = connector.getFileSystemService(); + fs.copy(src, tgt, false, false, new IFileSystem.DoneCopy() { + public void doneCopy(IToken token, FileSystemException error) { + if (error != null) error(error); + else done(true); + } + }); + } + }.getS(monitor, "Copy: " + srcName); + } + + public boolean copyBatch(String[] srcParents, + String[] srcNames, String tgtParent, IProgressMonitor monitor) throws SystemMessageException { + for (int i = 0; i < srcParents.length; i++) { + if (!copy(srcParents[i], srcNames[i], tgtParent, srcNames[i], monitor)) return false; + } + return true; + } + + public IHostFile createFile(String parent, + String name, IProgressMonitor monitor) throws SystemMessageException { + try { + getOutputStream(parent, name, true, monitor).close(); + return getFile(parent, name, monitor); + } + catch (IOException e) { + throw new SystemMessageException(getMessage(e)); + } + } + + public IHostFile createFolder(final String parent, final String name, IProgressMonitor monitor) throws SystemMessageException { + final String path = toRemotePath(parent, name); + return new TCFRSETask<IHostFile>() { + public void run() { + final IFileSystem fs = connector.getFileSystemService(); + fs.mkdir(path, null, new IFileSystem.DoneMkDir() { + public void doneMkDir(IToken token, FileSystemException error) { + if (error != null) { + error(error); + return; + } + fs.stat(path, new IFileSystem.DoneStat() { + public void doneStat(IToken token, + FileSystemException error, FileAttrs attrs) { + if (error != null) error(error); + else done(new TCFFileResource(TCFFileService.this, + path, null, attrs, false)); + } + }); + } + }); + } + }.getS(monitor, "Create folder"); + } + + public boolean delete(String parent, + String name, IProgressMonitor monitor) throws SystemMessageException { + final String path = toRemotePath(parent, name); + return new TCFRSETask<Boolean>() { + public void run() { + final IFileSystem fs = connector.getFileSystemService(); + fs.stat(path, new IFileSystem.DoneStat() { + public void doneStat(IToken token, + FileSystemException error, FileAttrs attrs) { + if (error != null) { + error(error); + return; + } + IFileSystem.DoneRemove done = new IFileSystem.DoneRemove() { + public void doneRemove(IToken token, FileSystemException error) { + if (error != null) { + error(error); + return; + } + done(true); + } + }; + if (attrs.isDirectory()) { + fs.rmdir(path, done); + } + else { + fs.remove(path, done); + } + } + }); + } + }.getS(monitor, "Delete"); + } + + public boolean deleteBatch(String[] remoteParents, String[] fileNames, IProgressMonitor monitor) + throws SystemMessageException { + for (int i = 0; i < remoteParents.length; i++) { + delete(remoteParents[i], fileNames[i], monitor); + } + return true; + } + + public boolean download(final String parent, + final String name, final File file, final boolean is_binary, + final String host_encoding, IProgressMonitor monitor) throws SystemMessageException { + monitor.beginTask("Download", 1); + try { + file.getParentFile().mkdirs(); + InputStream inp = getInputStream(parent, name, is_binary, new NullProgressMonitor()); + OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); + copyStream(inp, out, is_binary, "UTF8", host_encoding); + return true; + } + catch (Throwable x) { + if (x instanceof SystemMessageException) throw (SystemMessageException)x; + throw new SystemMessageException(getMessage(x)); + } + finally { + monitor.done(); + } + } + + public String getEncoding(IProgressMonitor monitor) throws SystemMessageException { + return "UTF8"; + } + + public IHostFile getFile(final String parent, + final String name, IProgressMonitor monitor) throws SystemMessageException { + final String path = toRemotePath(parent, name); + return new TCFRSETask<IHostFile>() { + public void run() { + IFileSystem fs = connector.getFileSystemService(); + fs.stat(path, new IFileSystem.DoneStat() { + public void doneStat(IToken token, + FileSystemException error, FileAttrs attrs) { + if (error != null) { + if (error.getStatus() == IFileSystem.STATUS_NO_SUCH_FILE) { + done(new TCFFileResource(TCFFileService.this, path, null, null, false)); + return; + } + error(error); + return; + } + done(new TCFFileResource(TCFFileService.this, path, null, attrs, false)); + } + }); + } + }.getS(monitor, "Stat"); + } + + protected IHostFile[] internalFetch(final String parent, final String filter, final int fileType, final IProgressMonitor monitor) + throws SystemMessageException { + final String path = toRemotePath(parent, null); + final boolean wantFiles = (fileType==FILE_TYPE_FILES_AND_FOLDERS || (fileType&FILE_TYPE_FILES)!=0); + final boolean wantFolders = (fileType==FILE_TYPE_FILES_AND_FOLDERS || (fileType%FILE_TYPE_FOLDERS)!=0); + return new TCFRSETask<IHostFile[]>() { + private IMatcher matcher = null; + public void run() { + if (filter == null) { + matcher = null; + } + else if (filter.endsWith(",")) { //$NON-NLS-1$ + String[] types = filter.split(","); //$NON-NLS-1$ + matcher = new FileTypeMatcher(types, true); + } + else { + matcher = new NamePatternMatcher(filter, true, true); + } + final List<TCFFileResource> results = new ArrayList<TCFFileResource>(); + final IFileSystem fs = connector.getFileSystemService(); + fs.opendir(path, new IFileSystem.DoneOpen() { + public void doneOpen(IToken token, FileSystemException error, final IFileHandle handle) { + if (error != null) { + error(error); + return; + } + fs.readdir(handle, new IFileSystem.DoneReadDir() { + public void doneReadDir(IToken token, + FileSystemException error, DirEntry[] entries, boolean eof) { + if (error != null) { + error(error); + return; + } + for (DirEntry e : entries) { + if (e.attrs == null) { + // Attrs are not available if, for example, + // the entry is a broken symbolic link + } + else if (e.attrs.isDirectory()) { + //dont filter folder names if getting both folders and files + if (wantFolders && (matcher==null || fileType==FILE_TYPE_FILES_AND_FOLDERS || matcher.matches(e.filename))) { + results.add(new TCFFileResource(TCFFileService.this, + path, e.filename, e.attrs, false)); + } + } + else if (e.attrs.isFile()) { + if (wantFiles && (matcher == null || matcher.matches(e.filename))) { + results.add(new TCFFileResource(TCFFileService.this, + path, e.filename, e.attrs, false)); + } + } + } + if (eof) { + fs.close(handle, new IFileSystem.DoneClose() { + public void doneClose(IToken token, FileSystemException error) { + if (error != null) { + error(error); + return; + } + done(results.toArray(new TCFFileResource[results.size()])); + } + }); + } + else { + fs.readdir(handle, this); + } + } + }); + } + }); + } + }.getS(monitor, "Get files and folders"); + } + + public InputStream getInputStream(final String parent, final String name, boolean isBinary, IProgressMonitor monitor) + throws SystemMessageException { + final String path = toRemotePath(parent, name); + final IFileHandle handle = new TCFRSETask<IFileHandle>() { + public void run() { + IFileSystem fs = connector.getFileSystemService(); + fs.open(path, IFileSystem.O_READ, null, new IFileSystem.DoneOpen() { + public void doneOpen(IToken token, FileSystemException error, IFileHandle handle) { + if (error != null) error(error); + else done(handle); + } + }); + } + }.getS(monitor, "Get input stream"); + return new TCFFileInputStream(handle); + } + + public OutputStream getOutputStream(final String parent, final String name, boolean isBinary, IProgressMonitor monitor) + throws SystemMessageException { + final String path = toRemotePath(parent, name); + final IFileHandle handle = new TCFRSETask<IFileHandle>() { + public void run() { + IFileSystem fs = connector.getFileSystemService(); + int flags = IFileSystem.O_WRITE | IFileSystem.O_CREAT | IFileSystem.O_TRUNC; + fs.open(path, flags, null, new IFileSystem.DoneOpen() { + public void doneOpen(IToken token, FileSystemException error, IFileHandle handle) { + if (error != null) error(error); + else done(handle); + } + }); + } + }.getS(monitor, "Get output stream"); + return new TCFFileOutputStream(handle); + } + + public IHostFile[] getRoots(IProgressMonitor monitor) throws SystemMessageException { + return new TCFRSETask<IHostFile[]>() { + public void run() { + IFileSystem fs = connector.getFileSystemService(); + fs.roots(new IFileSystem.DoneRoots() { + public void doneRoots(IToken token, FileSystemException error, DirEntry[] entries) { + if (error != null) { + error(error); + return; + } + List<TCFFileResource> l = new ArrayList<TCFFileResource>(); + for (DirEntry e : entries) { + if (e.attrs == null) continue; + l.add(new TCFFileResource(TCFFileService.this, "", e.filename, e.attrs, true)); + } + done(l.toArray(new IHostFile[l.size()])); + } + }); + } + }.getS(monitor, "Get roots"); + } + + public IHostFile getUserHome() { + UserInfo ui = getUserInfo(); + try { + return getFile(ui.home, ".", new NullProgressMonitor()); + } + catch (SystemMessageException e) { + throw new Error(e); + } + } + + public boolean isCaseSensitive() { + return true; + } + + public boolean move(final String srcParent, + final String srcName, final String tgtParent, final String tgtName, IProgressMonitor monitor) + throws SystemMessageException { + final String src_path = toRemotePath(srcParent, srcName); + final String tgt_path = toRemotePath(tgtParent, tgtName); + return new TCFRSETask<Boolean>() { + public void run() { + IFileSystem fs = connector.getFileSystemService(); + fs.rename(src_path, tgt_path, new IFileSystem.DoneRename() { + public void doneRename(IToken token, FileSystemException error) { + if (error != null) error(error); + else done(true); + } + }); + } + }.getS(monitor, "Move"); + } + + public boolean rename(String remoteParent, + String oldName, String newName, IProgressMonitor monitor) throws SystemMessageException { + return move(remoteParent, oldName, remoteParent, newName, monitor); + } + + public boolean rename(String remoteParent, + String oldName, String newName, IHostFile oldFile, IProgressMonitor monitor) + throws SystemMessageException { + boolean b = move(remoteParent, oldName, remoteParent, newName, monitor); + if (b) oldFile.renameTo(toRemotePath(remoteParent, newName)); + return b; + } + + public boolean setLastModified(final String parent, + final String name, final long timestamp, IProgressMonitor monitor) throws SystemMessageException { + final String path = toRemotePath(parent, name); + return new TCFRSETask<Boolean>() { + public void run() { + IFileSystem fs = connector.getFileSystemService(); + FileAttrs attrs = new FileAttrs(IFileSystem.ATTR_ACMODTIME, + 0, 0, 0, 0, timestamp, timestamp, null); + fs.setstat(path, attrs, new IFileSystem.DoneSetStat() { + public void doneSetStat(IToken token, FileSystemException error) { + if (error != null) error(error); + else done(true); + } + }); + } + }.getS(monitor, "Set modification time"); + } + + public boolean setReadOnly(final String parent, + final String name, final boolean readOnly, IProgressMonitor monitor) throws SystemMessageException { + final String path = toRemotePath(parent, name); + return new TCFRSETask<Boolean>() { + public void run() { + final IFileSystem fs = connector.getFileSystemService(); + fs.stat(path, new IFileSystem.DoneStat() { + public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) { + if (error != null) { + error(error); + return; + } + int p = attrs.permissions; + if (readOnly) { + p &= ~IFileSystem.S_IWUSR; + p &= ~IFileSystem.S_IWGRP; + p &= ~IFileSystem.S_IWOTH; + } + else { + p |= IFileSystem.S_IWUSR; + p |= IFileSystem.S_IWGRP; + p |= IFileSystem.S_IWOTH; + } + FileAttrs new_attrs = new FileAttrs(IFileSystem.ATTR_PERMISSIONS, + 0, 0, 0, p, 0, 0, null); + fs.setstat(path, new_attrs, new IFileSystem.DoneSetStat() { + public void doneSetStat(IToken token, FileSystemException error) { + if (error != null) error(error); + else done(true); + } + }); + } + }); + } + }.getS(monitor, "Set permissions"); + } + + public boolean upload(InputStream inp, + String parent, String name, boolean isBinary, + String hostEncoding, IProgressMonitor monitor) throws SystemMessageException { + monitor.beginTask("Upload", 1); + try { + OutputStream out = getOutputStream(parent, name, isBinary, new NullProgressMonitor()); + copyStream(inp, out, isBinary, hostEncoding, "UTF8"); + return true; + } + catch (Throwable x) { + if (x instanceof SystemMessageException) throw (SystemMessageException)x; + throw new SystemMessageException(getMessage(x)); + } + finally { + monitor.done(); + } + } + + public boolean upload(File localFile, + String parent, String name, boolean isBinary, + String srcEncoding, String hostEncoding, IProgressMonitor monitor) + throws SystemMessageException { + monitor.beginTask("Upload", 1); + try { + OutputStream out = getOutputStream(parent, name, isBinary, new NullProgressMonitor()); + InputStream inp = new BufferedInputStream(new FileInputStream(localFile)); + copyStream(inp, out, isBinary, hostEncoding, "UTF8"); + return true; + } + catch (Throwable x) { + if (x instanceof SystemMessageException) throw (SystemMessageException)x; + throw new SystemMessageException(getMessage(x)); + } + finally { + monitor.done(); + } + } + + private void copyStream(InputStream inp, OutputStream out, + boolean is_binary, String inp_encoding, String out_encoding) throws IOException { + try { + if (!is_binary) { + if (inp_encoding.equals("UTF-8")) inp_encoding = "UTF8"; + if (out_encoding.equals("UTF-8")) out_encoding = "UTF8"; + } + if (is_binary || inp_encoding.equals(out_encoding)) { + byte[] buf = new byte[0x1000]; + for (;;) { + int buf_len = inp.read(buf); + if (buf_len < 0) break; + out.write(buf, 0, buf_len); + } + } + else { + Reader reader = new InputStreamReader(inp, inp_encoding); + Writer writer = new OutputStreamWriter(out, out_encoding); + char[] buf = new char[0x1000]; + for (;;) { + int buf_len = reader.read(buf); + if (buf_len < 0) break; + writer.write(buf, 0, buf_len); + } + writer.flush(); + } + } + finally { + out.close(); + inp.close(); + } + } + + private synchronized UserInfo getUserInfo() { + if (user_info == null || user_info.error != null) { + user_info = new TCFRSETask<UserInfo>() { + public void run() { + IFileSystem fs = connector.getFileSystemService(); + fs.user(new IFileSystem.DoneUser() { + public void doneUser(IToken token, FileSystemException error, int real_uid, + int effective_uid, int real_gid, int effective_gid, String home) { + if (error != null) done(new UserInfo(error)); + else done(new UserInfo(real_uid, effective_uid, real_gid, effective_gid, home)); + } + }); + } + }.getE(); + } + return user_info; + } + + public boolean canRead(FileAttrs attrs) { + if ((attrs.flags & IFileSystem.ATTR_PERMISSIONS) == 0) return false; + if ((attrs.flags & IFileSystem.ATTR_UIDGID) == 0) return false; + UserInfo ui = getUserInfo(); + if (ui.error != null) return false; + if (ui.e_uid == attrs.uid) { + return (attrs.permissions & IFileSystem.S_IRUSR) != 0; + } + if (ui.e_gid == attrs.gid) { + return (attrs.permissions & IFileSystem.S_IRGRP) != 0; + } + return (attrs.permissions & IFileSystem.S_IROTH) != 0; + } + + public boolean canWrite(FileAttrs attrs) { + if ((attrs.flags & IFileSystem.ATTR_PERMISSIONS) == 0) return false; + if ((attrs.flags & IFileSystem.ATTR_UIDGID) == 0) return false; + UserInfo ui = getUserInfo(); + if (ui.error != null) return false; + if (ui.e_uid == attrs.uid) { + return (attrs.permissions & IFileSystem.S_IWUSR) != 0; + } + if (ui.e_gid == attrs.gid) { + return (attrs.permissions & IFileSystem.S_IWGRP) != 0; + } + return (attrs.permissions & IFileSystem.S_IWOTH) != 0; + } + +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileSubSystemConfiguration.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileSubSystemConfiguration.java new file mode 100644 index 000000000..af6678aef --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFFileSubSystemConfiguration.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.util.Vector; + +import org.eclipse.rse.core.filters.ISystemFilter; +import org.eclipse.rse.core.filters.ISystemFilterPool; +import org.eclipse.rse.core.filters.ISystemFilterPoolManager; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.core.subsystems.IConnectorService; +import org.eclipse.rse.core.subsystems.ISubSystem; +import org.eclipse.rse.services.clientserver.SystemSearchString; +import org.eclipse.rse.services.files.IFileService; +import org.eclipse.rse.services.search.IHostSearchResultConfiguration; +import org.eclipse.rse.services.search.IHostSearchResultSet; +import org.eclipse.rse.services.search.ISearchService; +import org.eclipse.rse.subsystems.files.core.ILanguageUtilityFactory; +import org.eclipse.rse.subsystems.files.core.model.RemoteFileFilterString; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.FileServiceSubSystem; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.FileServiceSubSystemConfiguration; +import org.eclipse.rse.subsystems.files.core.subsystems.IHostFileToRemoteFileAdapter; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileSubSystem; +import org.eclipse.rse.ui.SystemBasePlugin; + +public class TCFFileSubSystemConfiguration extends FileServiceSubSystemConfiguration { + + private final TCFFileAdapter file_adapter = new TCFFileAdapter(); + + @Override + public ISubSystem createSubSystemInternal(IHost host) { + TCFConnectorService connectorService = (TCFConnectorService)getConnectorService(host); + return new FileServiceSubSystem(host, connectorService, + getFileService(host), getHostFileAdapter(), createSearchService(host)); + } + + public IFileService createFileService(IHost host) { + return new TCFFileService(host); + } + + public IHostSearchResultConfiguration createSearchConfiguration(IHost host, + IHostSearchResultSet resultSet, Object searchTarget, + SystemSearchString searchString) { + // TODO Auto-generated method stub + return null; + } + + public ISearchService createSearchService(IHost host) { + // TODO Auto-generated method stub + return null; + } + + public IHostFileToRemoteFileAdapter getHostFileAdapter() { + return file_adapter; + } + + public ILanguageUtilityFactory getLanguageUtilityFactory(IRemoteFileSubSystem ss) { + // TODO Auto-generated method stub + return null; + } + + public boolean supportsArchiveManagement() { + return false; + } + + public IConnectorService getConnectorService(IHost host) { + return TCFConnectorServiceManager.getInstance() + .getConnectorService(host, ITCFSubSystem.class); + } + + @SuppressWarnings("unchecked") + public Class getServiceImplType() { + return TCFFileService.class; + } + + public void setConnectorService(IHost host, IConnectorService connectorService) { + TCFConnectorServiceManager.getInstance().setConnectorService(host, getServiceImplType(), connectorService); + } + + protected ISystemFilterPool createDefaultFilterPool(ISystemFilterPoolManager mgr) { + ISystemFilterPool pool = null; + try { + pool = mgr.createSystemFilterPool(getDefaultFilterPoolName(mgr.getName(), getId()), true); + + Vector<String> filterStrings = new Vector<String>(); + RemoteFileFilterString myHomeFilterString = new RemoteFileFilterString(this); + myHomeFilterString.setPath("."); + myHomeFilterString.setFile("*"); + filterStrings.add(myHomeFilterString.toString()); + ISystemFilter filter = mgr.createSystemFilter(pool, "Home", filterStrings); + filter.setNonChangable(true); + filter.setSingleFilterStringOnly(true); + + filterStrings = new Vector<String>(); + RemoteFileFilterString rootFilesFilterString = new RemoteFileFilterString(this); + rootFilesFilterString.setPath(""); + rootFilesFilterString.setFile("*"); + filterStrings.add(rootFilesFilterString.toString()); + filter = mgr.createSystemFilter(pool, "Root", filterStrings); + filter.setNonChangable(true); + filter.setSingleFilterStringOnly(true); + } + catch (Exception exc) { + SystemBasePlugin.logError("Error creating default filter pool", exc); //$NON-NLS-1$ + } + return pool; + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessAdapter.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessAdapter.java new file mode 100644 index 000000000..3748cda67 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessAdapter.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.rse.services.clientserver.processes.IHostProcess; +import org.eclipse.rse.subsystems.processes.core.subsystem.IHostProcessToRemoteProcessAdapter; +import org.eclipse.rse.subsystems.processes.core.subsystem.IRemoteProcess; +import org.eclipse.rse.subsystems.processes.core.subsystem.IRemoteProcessContext; + +public class TCFProcessAdapter implements IHostProcessToRemoteProcessAdapter { + + public IRemoteProcess convertToRemoteProcess(IRemoteProcessContext context, + IRemoteProcess parent, IHostProcess node) { + IHostProcess[] nodes = new IHostProcess[]{ node }; + IRemoteProcess[] processes = convertToRemoteProcesses(context, parent, nodes); + if (processes != null && processes.length > 0) return processes[0]; + return null; + } + + public IRemoteProcess[] convertToRemoteProcesses( + IRemoteProcessContext context, IRemoteProcess parent, + IHostProcess[] nodes) { + + if (nodes == null)return null; + List<IRemoteProcess> list = new ArrayList<IRemoteProcess>(nodes.length); + for (int idx = 0; idx < nodes.length; idx++) { + TCFProcessResource node = (TCFProcessResource)nodes[idx]; + list.add(new TCFRemoteProcess(context, node)); + } + return list.toArray(new IRemoteProcess[list.size()]); + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessResource.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessResource.java new file mode 100644 index 000000000..b46d4e4a5 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessResource.java @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.math.BigInteger; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.rse.core.subsystems.AbstractResource; +import org.eclipse.rse.services.clientserver.processes.IHostProcess; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.protocol.Protocol; +import com.windriver.tcf.api.services.ISysMonitor; +import com.windriver.tcf.api.services.ISysMonitor.SysMonitorContext; + +public class TCFProcessResource extends AbstractResource implements IHostProcess { + + private final TCFProcessService rse_service; + private final ISysMonitor tcf_service; + private final TCFProcessResource prev; + private final String id; + private Throwable error; + private ISysMonitor.SysMonitorContext context; + + private final List<Runnable> children_wait_list = new ArrayList<Runnable>(); + private boolean children_loading; + private boolean children_loaded; + private Throwable children_error; + private boolean running_wait_list; + + private long gid = -1; + private String name; + private long ppid = -1; + private long pid = -1; + private String state; + private long tgid = -1; + private long tracepid = -1; + private long uid; + private String username; + private long vm_rss_kb; + private long vm_size_kb; + private String utime_pc; + private String stime_pc; + private long timestamp; + + private Map<String, Object> properties = new HashMap<String, Object>(); + + private static final NumberFormat percent_format; + + static { + percent_format = NumberFormat.getPercentInstance(); + percent_format.setMaximumFractionDigits(3); + } + + TCFProcessResource(TCFProcessService rse_service, ISysMonitor service, + TCFProcessResource prev, String id) { + this.rse_service = rse_service; + this.tcf_service = service; + this.prev = prev; + this.id = id; + } + + public String getID() { + return id; + } + + public String getParentID() { + return (String)properties.get(ISysMonitor.PROP_PARENTID); + } + + public TCFProcessService getService() { + return rse_service; + } + + public void invalidate() { + assert Protocol.isDispatchThread(); + error = null; + context = null; + } + + public boolean validate(final Runnable done) { + assert Protocol.isDispatchThread(); + if (error != null) return true; + if (context != null) return true; + tcf_service.getContext(id, new ISysMonitor.DoneGetContext() { + + public void doneGetContext(IToken token, Exception error, SysMonitorContext context) { + TCFProcessResource.this.error = error; + TCFProcessResource.this.context = context; + if (error != null) { + properties = new HashMap<String, Object>(); + gid = -1; + name = null; + ppid = -1; + pid = -1; + state = null; + tgid = -1; + tracepid = -1; + uid = 0; + username = null; + vm_rss_kb = 0; + vm_size_kb = 0; + } + else { + properties = new HashMap<String, Object>(context.getProperties()); + gid = context.getUGID(); + name = context.getFile(); + if (properties.containsKey(ISysMonitor.PROP_PPID)) { + ppid = context.getPPID(); + } + pid = context.getPID(); + state = context.getState(); + tgid = context.getTGID(); + if (properties.containsKey(ISysMonitor.PROP_TRACERPID)) { + tracepid = context.getTracerPID(); + } + uid = context.getUID(); + username = context.getUserName(); + vm_rss_kb = context.getRSS(); + long page_bytes = context.getPSize(); + if (page_bytes <= 0 || vm_rss_kb < 0) { + vm_rss_kb = -1; + } + else { + vm_rss_kb = (vm_rss_kb * page_bytes + 1023) / 1024; + } + vm_size_kb = (context.getVSize() + 1023) / 1024; + } + timestamp = System.currentTimeMillis(); + Protocol.invokeLater(done); + } + + }); + return false; + } + + public Throwable getError() { + assert Protocol.isDispatchThread(); + return error; + } + + public ISysMonitor.SysMonitorContext getContext() { + assert Protocol.isDispatchThread(); + return context; + } + + // IHostProcess methods + + public String getAllProperties() { + // TODO Auto-generated method stub + return null; + } + + public long getGid() { + return gid; + } + + public String getLabel() { + return Long.toString(getPid()) + " " + name; + } + + public String getName() { + return name; + } + + public long getPPid() { + return ppid; + } + + public long getPid() { + return pid; + } + + public String getState() { + return state; + } + + public long getTgid() { + return tgid; + } + + public long getTracerPid() { + return tracepid; + } + + public long getUid() { + return uid; + } + + public String getUsername() { + return username; + } + + public long getVmRSSInKB() { + return vm_rss_kb; + } + + public long getVmSizeInKB() { + return vm_size_kb; + } + + public boolean isRoot() { + return true; + } + + String getStatusLine() { + final String STATUS_DELIMITER = "|"; //$NON-NLS-1$ + StringBuffer s = new StringBuffer(); + s.append(getPid()).append(STATUS_DELIMITER); + s.append(name).append(STATUS_DELIMITER); + s.append(getState()).append(STATUS_DELIMITER); + s.append(getTgid()).append(STATUS_DELIMITER); + s.append(getPPid()).append(STATUS_DELIMITER); + s.append('0').append(STATUS_DELIMITER); + s.append(getUid()).append(STATUS_DELIMITER); + s.append(getUsername()).append(STATUS_DELIMITER); + s.append(getGid()).append(STATUS_DELIMITER); + s.append(getVmSizeInKB()).append(STATUS_DELIMITER); + s.append(getVmRSSInKB()).append(STATUS_DELIMITER); + return s.toString(); + } + + public Map<String,Object> getProperties() { + return properties; + } + + private String getTimePC(String name) { + if (prev == null) return null; + Object x = prev.properties.get(name); + Object y = properties.get(name); + if (x instanceof Number && y instanceof Number) { + BigInteger nx = x instanceof BigInteger ? (BigInteger)x : new BigInteger(x.toString()); + BigInteger ny = y instanceof BigInteger ? (BigInteger)y : new BigInteger(y.toString()); + double d = ny.subtract(nx).doubleValue() / (timestamp - prev.timestamp); + return percent_format.format(d); + } + return null; + } + + public String getUserTimePC() { + if (utime_pc != null) return utime_pc; + return utime_pc = getTimePC(ISysMonitor.PROP_UTIME); + } + + public String getSysTimePC() { + if (stime_pc != null) return stime_pc; + return stime_pc = getTimePC(ISysMonitor.PROP_STIME); + } + + public boolean getChildrenLoading() { + return children_loading; + } + + public void setChildrenLoading(boolean b) { + children_loading = b; + } + + public boolean getChildrenLoaded() { + return children_loaded; + } + + public void setChildrenLoaded(boolean b) { + children_loaded = b; + } + + public Throwable getChildrenError() { + return children_error; + } + + public void setChildrenError(Throwable error) { + children_error = error; + } + + public void addChildrenWaitList(Runnable run) { + assert !running_wait_list; + assert children_loading; + assert !children_loaded; + children_wait_list.add(run); + } + + public void runChildrenWaitList() { + assert !children_loading; + assert children_loaded; + try { + running_wait_list = true; + for (Runnable r : children_wait_list) r.run(); + children_wait_list.clear(); + } + finally { + running_wait_list = false; + } + } + + public void cancelChildrenLoading() { + // TODO: cancelChildrenLoading + } + + public String toString() { + return "[" + getStatusLine() + "]"; + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessService.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessService.java new file mode 100644 index 000000000..4f3445b9f --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessService.java @@ -0,0 +1,275 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.services.clientserver.messages.IndicatorException; +import org.eclipse.rse.services.clientserver.messages.SystemMessage; +import org.eclipse.rse.services.clientserver.messages.SystemMessageException; +import org.eclipse.rse.services.clientserver.processes.HostProcessFilterImpl; +import org.eclipse.rse.services.clientserver.processes.IHostProcess; +import org.eclipse.rse.services.clientserver.processes.IHostProcessFilter; +import org.eclipse.rse.services.processes.IProcessService; + +import com.windriver.tcf.api.protocol.IToken; +import com.windriver.tcf.api.services.ISysMonitor; + +public class TCFProcessService implements IProcessService { + + private final TCFConnectorService connector; + private final TCFProcessResource root; + private final Map<String,TCFProcessResource> id2res = new HashMap<String,TCFProcessResource>(); + private final Map<Long,TCFProcessResource> pid2res = new HashMap<Long,TCFProcessResource>(); + + public TCFProcessService(IHost host) { + connector = (TCFConnectorService)TCFConnectorServiceManager + .getInstance().getConnectorService(host, ITCFSubSystem.class); + root = new TCFProcessResource(this, null, null, null); + } + + public String getDescription() { + return "The TCF Process Service uses the Target Communication Framework to provide service for the Processes subsystem." + + " It requires a TCF agent to be running on the remote machine."; + } + + public SystemMessage getMessage(String messageID) { + try { + return new SystemMessage("TCF", "C", "0001", + SystemMessage.ERROR, messageID, null); + } + catch (IndicatorException e) { + throw new Error(e); + } + } + + public String getName() { + return "TCF Process Service"; + } + + public void initService(IProgressMonitor monitor) { + } + + public void uninitService(IProgressMonitor monitor) { + } + + public IHostProcess getParentProcess(long PID, IProgressMonitor monitor) + throws SystemMessageException { + return getProcess(getProcess(PID, monitor).getPPid(), monitor); + } + + public IHostProcess getProcess(final long PID, IProgressMonitor monitor) + throws SystemMessageException { + return new TCFRSETask<IHostProcess>() { + public void run() { + if (!loadProcesses(this, root)) return; + if (root.getChildrenError() != null) { + error(root.getChildrenError()); + return; + } + done(pid2res.get(PID)); + } + }.getS(monitor, "Get process properties"); + } + + public String[] getSignalTypes() { + // TODO Auto-generated method stub + return null; + } + + public boolean kill(long PID, String signal, IProgressMonitor monitor) + throws SystemMessageException { + // TODO Auto-generated method stub + return false; + } + + private void sort(IHostProcess[] arr) { + Comparator<IHostProcess> c = new Comparator<IHostProcess>() { + public int compare(IHostProcess o1, IHostProcess o2) { + long p1 = o1.getPid(); + long p2 = o2.getPid(); + if (p1 < p2) return -1; + if (p1 > p2) return 1; + return 0; + } + }; + Arrays.sort(arr, c); + } + + public IHostProcess[] listAllProcesses(IProgressMonitor monitor) throws SystemMessageException { + HostProcessFilterImpl rpfs = new HostProcessFilterImpl(); + return listAllProcesses(rpfs, monitor); + } + + public IHostProcess[] listAllProcesses(final IHostProcessFilter filter, IProgressMonitor monitor) throws SystemMessageException { + return listAllProcesses(filter, root, monitor); + } + + private boolean eqaulIDs(String x, String y) { + if (x == null) return y == null; + return x.equals(y); + } + + public IHostProcess[] listAllProcesses(final IHostProcessFilter filter, final IHostProcess up, IProgressMonitor monitor) throws SystemMessageException { + // TODO: figure out better way to flush the cache + final TCFProcessResource parent = new TCFRSETask<TCFProcessResource>() { + public void run() { + TCFProcessResource parent = (TCFProcessResource)up; + if (parent == null) { + error(new IOException("Invalid parent")); + return; + } + if (filter.getPpid() != null && filter.getPpid().equals("*") || parent.getChildrenError() != null) { + for (Iterator<TCFProcessResource> i = pid2res.values().iterator(); i.hasNext();) { + TCFProcessResource r = i.next(); + if (eqaulIDs(parent.getID(), r.getParentID())) { + i.remove(); + if (r.getChildrenLoaded()) r.cancelChildrenLoading(); + } + } + parent.setChildrenLoaded(false); + parent.setChildrenError(null); + } + done(parent); + } + }.getS(monitor, "Flush processes cache"); + return new TCFRSETask<IHostProcess[]>() { + public void run() { + if (!loadProcesses(this, parent)) return; + if (parent.getChildrenError() != null) { + error(parent.getChildrenError()); + return; + } + List<IHostProcess> l = new ArrayList<IHostProcess>(); + for (TCFProcessResource p : pid2res.values()) { + if (eqaulIDs(parent.getID(), p.getParentID())) { + Throwable error = p.getError(); + if (error == null && filter.allows(p.getStatusLine())) l.add(p); + } + } + IHostProcess[] arr = new IHostProcess[l.size()]; + l.toArray(arr); + sort(arr); + done(arr); + } + }.getS(monitor, "List processes"); + } + + public IHostProcess[] listAllProcesses(String exeNameFilter, String userNameFilter, String stateFilter, IProgressMonitor monitor) + throws SystemMessageException { + HostProcessFilterImpl rpfs = new HostProcessFilterImpl(); + rpfs.setName(exeNameFilter); + rpfs.setUsername(userNameFilter); + rpfs.setSpecificState(stateFilter); + return listAllProcesses(rpfs, monitor); + } + + public IHostProcess[] listChildProcesses(long parentPID, IProgressMonitor monitor) throws SystemMessageException { + HostProcessFilterImpl rpfs = new HostProcessFilterImpl(); + return listChildProcesses(parentPID, rpfs, monitor); + } + + public IHostProcess[] listChildProcesses(long parentPID, IHostProcessFilter filter, IProgressMonitor monitor) + throws SystemMessageException { + filter.setPpid(Long.toString(parentPID)); + return listAllProcesses(filter, monitor); + } + + public IHostProcess[] listRootProcesses(IProgressMonitor monitor) + throws SystemMessageException { + IHostProcess[] roots = new IHostProcess[1]; + roots[0] = getProcess(1, monitor); + return roots; + } + + private boolean loadProcesses(Runnable run, final TCFProcessResource parent) { + if (parent.getChildrenLoading()) { + parent.addChildrenWaitList(run); + return false; + } + if (parent.getChildrenLoaded()) { + return true; + } + parent.setChildrenLoading(true); + try { + final ISysMonitor m = connector.getSysMonitorService(); + m.getChildren(parent.getID(), new ISysMonitor.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] ids) { + try { + if (error != null) { + loadProcessesDone(parent, error, null); + } + else if (ids == null) { + loadProcessesDone(parent, null, new TCFProcessResource[0]); + } + else { + final TCFProcessResource[] arr = new TCFProcessResource[ids.length]; + final Set<IHostProcess> pending = new HashSet<IHostProcess>(); + for (int i = 0; i < ids.length; i++) { + final TCFProcessResource r = new TCFProcessResource( + TCFProcessService.this, m, id2res.get(ids[i]), ids[i]); + if (!r.validate(new Runnable() { + public void run() { + pending.remove(r); + if (pending.isEmpty()) loadProcessesDone(parent, null, arr); + } + })) pending.add(r); + arr[i] = r; + } + if (pending.isEmpty()) loadProcessesDone(parent, null, arr); + } + } + catch (Throwable x) { + loadProcessesDone(parent, x, null); + } + } + }); + parent.addChildrenWaitList(run); + return false; + } + catch (Throwable x) { + loadProcessesDone(parent, x, null); + return true; + } + } + + private void loadProcessesDone(TCFProcessResource parent, Throwable error, TCFProcessResource[] arr) { + assert parent.getChildrenLoading(); + parent.setChildrenLoading(false); + parent.setChildrenLoaded(true); + if (arr != null && error == null) { + for (TCFProcessResource r : arr) { + long pid = r.getPid(); + if (pid > 0 && parent.getPid() != pid) { + pid2res.put(new Long(pid), r); + id2res.put(r.getID(), r); + } + } + } + for (Iterator<TCFProcessResource> i = id2res.values().iterator(); i.hasNext();) { + TCFProcessResource r = i.next(); + if (pid2res.get(r.getPid()) == null) i.remove(); + } + parent.setChildrenError(error); + parent.runChildrenWaitList(); + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessSubSystemConfiguration.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessSubSystemConfiguration.java new file mode 100644 index 000000000..0c7242051 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFProcessSubSystemConfiguration.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.core.subsystems.IConnectorService; +import org.eclipse.rse.core.subsystems.ISubSystem; +import org.eclipse.rse.services.processes.IProcessService; +import org.eclipse.rse.subsystems.processes.core.subsystem.IHostProcessToRemoteProcessAdapter; +import org.eclipse.rse.subsystems.processes.servicesubsystem.ProcessServiceSubSystem; +import org.eclipse.rse.subsystems.processes.servicesubsystem.ProcessServiceSubSystemConfiguration; + +public class TCFProcessSubSystemConfiguration extends ProcessServiceSubSystemConfiguration { + + private final TCFProcessAdapter process_adapter = new TCFProcessAdapter(); + + @SuppressWarnings("unchecked") + public Class getServiceImplType() { + return TCFProcessService.class; + } + + @Override + public ISubSystem createSubSystemInternal(IHost host) { + TCFConnectorService connectorService = (TCFConnectorService)getConnectorService(host); + return new ProcessServiceSubSystem(host, connectorService, + getProcessService(host), getHostProcessAdapter()); + } + + public IProcessService createProcessService(IHost host) { + return new TCFProcessService(host); + } + + public IHostProcessToRemoteProcessAdapter getHostProcessAdapter() { + return process_adapter; + } + + public IConnectorService getConnectorService(IHost host) { + return TCFConnectorServiceManager.getInstance() + .getConnectorService(host, ITCFSubSystem.class); + } + + public void setConnectorService(IHost host, IConnectorService connectorService) { + TCFConnectorServiceManager.getInstance().setConnectorService(host, getServiceImplType(), connectorService); + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRSETask.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRSETask.java new file mode 100644 index 000000000..9a37e4b6e --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRSETask.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.ExecutionException; + +import com.windriver.tcf.api.util.TCFTask; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.rse.services.clientserver.messages.IndicatorException; +import org.eclipse.rse.services.clientserver.messages.SystemMessage; +import org.eclipse.rse.services.clientserver.messages.SystemMessageException; + +public abstract class TCFRSETask<V> extends TCFTask<V> { + + public V get(IProgressMonitor monitor, String task_name) + throws InterruptedException, ExecutionException { + monitor.beginTask(task_name, 1); + try { + return get(); + } + finally { + monitor.done(); + } + } + + public V getS(IProgressMonitor monitor, String task_name) throws SystemMessageException { + if (monitor != null) monitor.beginTask(task_name, 1); + try { + return get(); + } + catch (Throwable e) { + if (e instanceof SystemMessageException) throw (SystemMessageException)e; + try { + SystemMessage m = new SystemMessage("TCF", "C", "0001", SystemMessage.ERROR, + e.getClass().getName(), e.getMessage()); + throw new SystemMessageException(m); + } + catch (IndicatorException e1) { + throw new Error(e1); + } + } + finally { + if (monitor != null) monitor.done(); + } + } + + public V getI(IProgressMonitor monitor, String task_name) throws InvocationTargetException, InterruptedException { + if (monitor != null) monitor.beginTask(task_name, 1); + try { + return get(); + } + catch (Throwable e) { + if (e instanceof InvocationTargetException) throw (InvocationTargetException)e; + if (e instanceof InterruptedException) throw (InterruptedException)e; + throw new InvocationTargetException(e); + } + finally { + if (monitor != null) monitor.done(); + } + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRemoteFile.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRemoteFile.java new file mode 100644 index 000000000..6b396d610 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRemoteFile.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.rse.services.files.IHostFile; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.AbstractRemoteFile; +import org.eclipse.rse.subsystems.files.core.servicesubsystem.FileServiceSubSystem; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileContext; + +public class TCFRemoteFile extends AbstractRemoteFile { + + public TCFRemoteFile(FileServiceSubSystem ss, IRemoteFileContext ctx, IRemoteFile parent, IHostFile file) { + super(ss, ctx, parent, file); + } + + public String getCanonicalPath() { + // TODO Auto-generated method stub + return null; + } + + public String getClassification() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRemoteProcess.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRemoteProcess.java new file mode 100644 index 000000000..d44b7200c --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFRemoteProcess.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.util.Map; + +import org.eclipse.rse.services.clientserver.processes.IHostProcess; +import org.eclipse.rse.subsystems.processes.core.subsystem.IRemoteProcessContext; +import org.eclipse.rse.subsystems.processes.core.subsystem.impl.RemoteProcessImpl; + +public class TCFRemoteProcess extends RemoteProcessImpl { + + public TCFRemoteProcess(IRemoteProcessContext context, IHostProcess process) { + super(context, process); + assert process != null; + } + + public Object getObject() { + return _underlyingProcess; + } + + public Map<String,Object> getProperties() { + return ((TCFProcessResource)_underlyingProcess).getProperties(); + } + + public String getUserTimePC() { + return ((TCFProcessResource)_underlyingProcess).getUserTimePC(); + } + + public String getSysTimePC() { + return ((TCFProcessResource)_underlyingProcess).getSysTimePC(); + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFSystemViewProcessAdapterFactory.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFSystemViewProcessAdapterFactory.java new file mode 100644 index 000000000..b97047796 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFSystemViewProcessAdapterFactory.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.rse.core.subsystems.IRemoteObjectIdentifier; +import org.eclipse.rse.core.subsystems.ISystemDragDropAdapter; +import org.eclipse.rse.ui.view.ISystemRemoteElementAdapter; +import org.eclipse.rse.ui.view.ISystemViewElementAdapter; +import org.eclipse.ui.IActionFilter; +import org.eclipse.ui.model.IWorkbenchAdapter; +import org.eclipse.ui.progress.IDeferredWorkbenchAdapter; +import org.eclipse.ui.views.properties.IPropertySource; + +public class TCFSystemViewProcessAdapterFactory implements IAdapterFactory { + + private final TCFSystemViewRemoteProcessAdapter adapter = + new TCFSystemViewRemoteProcessAdapter(); + + @SuppressWarnings("unchecked") + public Object getAdapter(Object adaptableObject, Class adapterType) { + assert adaptableObject instanceof TCFRemoteProcess; + if (adapterType == IPropertySource.class) { + ((ISystemViewElementAdapter)adapter).setPropertySourceInput(adaptableObject); + } + return adapter; + } + + @SuppressWarnings("unchecked") + public Class[] getAdapterList() { + return new Class[] { + ISystemViewElementAdapter.class, + ISystemDragDropAdapter.class, + ISystemRemoteElementAdapter.class, + IPropertySource.class, + IWorkbenchAdapter.class, + IActionFilter.class, + IDeferredWorkbenchAdapter.class, + IRemoteObjectIdentifier.class, + }; + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFSystemViewRemoteProcessAdapter.java b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFSystemViewRemoteProcessAdapter.java new file mode 100644 index 000000000..748f262ab --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/TCFSystemViewRemoteProcessAdapter.java @@ -0,0 +1,446 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package com.windriver.tcf.rse.ui; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.rse.core.model.ISystemMessageObject; +import org.eclipse.rse.core.model.ISystemResourceSet; +import org.eclipse.rse.core.model.SystemMessageObject; +import org.eclipse.rse.core.model.SystemRemoteResourceSet; +import org.eclipse.rse.core.subsystems.ISubSystem; +import org.eclipse.rse.services.clientserver.processes.HostProcessFilterImpl; +import org.eclipse.rse.services.clientserver.processes.IHostProcess; +import org.eclipse.rse.services.clientserver.processes.IHostProcessFilter; +import org.eclipse.rse.services.clientserver.processes.ISystemProcessRemoteTypes; +import org.eclipse.rse.subsystems.processes.core.subsystem.IRemoteProcess; +import org.eclipse.rse.subsystems.processes.core.subsystem.IRemoteProcessSubSystem; +import org.eclipse.rse.ui.ISystemMessages; +import org.eclipse.rse.ui.RSEUIPlugin; +import org.eclipse.rse.ui.SystemBasePlugin; +import org.eclipse.rse.ui.SystemMenuManager; +import org.eclipse.rse.ui.actions.SystemCopyToClipboardAction; +import org.eclipse.rse.ui.view.AbstractSystemViewAdapter; +import org.eclipse.rse.ui.view.ISystemRemoteElementAdapter; +import org.eclipse.rse.ui.view.ISystemViewElementAdapter; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.views.properties.IPropertyDescriptor; + +import com.windriver.tcf.api.services.ISysMonitor; + +public class TCFSystemViewRemoteProcessAdapter extends AbstractSystemViewAdapter + implements ISystemViewElementAdapter, ISystemRemoteElementAdapter{ + + private SystemCopyToClipboardAction copyClipboardAction; + private static final Object[] EMPTY_LIST = new Object[0]; + private static IPropertyDescriptor[] properties = null; + //private SystemKillProcessAction killProcessAction; + + private static final String PROP_PC_UTIME = "PCUTime"; + private static final String PROP_PC_STIME = "PCSTime"; + + public boolean canDrag(Object element) { + return true; + } + + public boolean canDrag(SystemRemoteResourceSet elements) { + return true; + } + + public Object doDrag(Object element, boolean sameSystemType, IProgressMonitor monitor) { + return getText(element); + } + + public ISystemResourceSet doDrag(SystemRemoteResourceSet set, IProgressMonitor monitor) { + return set; + } + + public void addActions(SystemMenuManager menu, + IStructuredSelection selection, Shell parent, String menuGroup) { + /* + if (killProcessAction == null) + killProcessAction = new SystemKillProcessAction(shell); + menu.add(ISystemContextMenuConstants.GROUP_CHANGE, killProcessAction); + */ + + if (copyClipboardAction == null) { + Clipboard clipboard = RSEUIPlugin.getTheSystemRegistryUI().getSystemClipboard(); + copyClipboardAction = new SystemCopyToClipboardAction(shell, clipboard); + } + menu.add(menuGroup, copyClipboardAction); + } + + public ISubSystem getSubSystem(Object element) { + if (element instanceof IRemoteProcess) { + IRemoteProcess process = (IRemoteProcess)element; + return process.getParentRemoteProcessSubSystem(); + } + return super.getSubSystem(element); + } + + public ImageDescriptor getImageDescriptor(Object element) { + IRemoteProcess process = (IRemoteProcess)element; + TCFProcessResource r = (TCFProcessResource)process.getObject(); + String state = r.getState(); + if (r.getParentID() != null) { + if (state.indexOf('R') >= 0) { + return Activator.getDefault().getImageDescriptorFromPath("icons/thread-r.gif"); //$NON-NLS-1$ + } + return Activator.getDefault().getImageDescriptorFromPath("icons/thread-s.gif"); //$NON-NLS-1$ + } + else { + if (state.indexOf('R') >= 0) { + return Activator.getDefault().getImageDescriptorFromPath("icons/process-r.gif"); //$NON-NLS-1$ + } + return Activator.getDefault().getImageDescriptorFromPath("icons/process-s.gif"); //$NON-NLS-1$ + } + } + + public String getText(Object element) { + String text = ((IRemoteProcess)element).getLabel(); + return (text == null) ? "" : text; //$NON-NLS-1$ + } + + public String getAlternateText(Object element) { + IRemoteProcess process = (IRemoteProcess)element; + String allProperties = process.getAllProperties(); + return allProperties.replace('|', '\t'); + } + + public String getAbsoluteName(Object object) { + IRemoteProcess process = (IRemoteProcess) object; + return "" + process.getPid(); //$NON-NLS-1$ + } + + public String getType(Object element) { + return "Process"; + } + + public Object getParent(Object element) { + IRemoteProcess process = (IRemoteProcess) element; + IRemoteProcess parent = process.getParentRemoteProcess(); + if ((parent != null) && parent.getAbsolutePath().equals(process.getAbsolutePath())) + // should never happen but sometimes it does, leading to infinite loop. + parent = null; + return parent; + } + + public boolean hasChildren(IAdaptable element) { + return getChildren(element, new NullProgressMonitor()).length > 0; + } + + public Object[] getChildren(IAdaptable element, IProgressMonitor monitor) { + IRemoteProcess process = (IRemoteProcess)element; + IRemoteProcessSubSystem ss = process.getParentRemoteProcessSubSystem(); + IHostProcessFilter orgRpfs = process.getFilterString(); + IHostProcessFilter newRpfs = new HostProcessFilterImpl(orgRpfs.toString()); + + Object[] children1 = null; + Object[] children2 = null; + newRpfs.setPpid(Long.toString(process.getPid())); + + try { + TCFProcessResource r = (TCFProcessResource)process.getObject(); + IHostProcess[] nodes = r.getService().listAllProcesses(orgRpfs, r, monitor); + TCFProcessAdapter adapter = new TCFProcessAdapter(); + children1 = adapter.convertToRemoteProcesses(process.getContext(), process, nodes); + if (children1 == null) children1 = EMPTY_LIST; + + children2 = ss.listAllProcesses(newRpfs, process.getContext(), monitor); + if (children2 == null) children2 = EMPTY_LIST; + } + catch (Exception exc) { + children1 = new SystemMessageObject[1]; + children1[0] = new SystemMessageObject(RSEUIPlugin.getPluginMessage(ISystemMessages.MSG_EXPAND_FAILED), ISystemMessageObject.MSGTYPE_ERROR, element); + children2 = null; + SystemBasePlugin.logError("Exception resolving file filter strings", exc); //$NON-NLS-1$ + } + if (children1 == null || children1.length == 0) return children2; + if (children2 == null || children2.length == 0) return children1; + Object[] children = new Object[children1.length + children2.length]; + System.arraycopy(children1, 0, children, 0, children1.length); + System.arraycopy(children2, 0, children, children1.length, children2.length); + return children; + } + + protected IPropertyDescriptor[] internalGetPropertyDescriptors() { + if (properties != null) return properties; + List<IPropertyDescriptor> l = new ArrayList<IPropertyDescriptor>(); + + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_ID, Messages.PROCESS_ID_LABEL, Messages.PROCESS_ID_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_PID, Messages.PROCESS_PID_LABEL, Messages.PROCESS_PID_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_FILE, Messages.PROCESS_NAME_LABEL, Messages.PROCESS_NAME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_CWD, Messages.PROCESS_CWD_LABEL, Messages.PROCESS_CWD_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_ROOT, Messages.PROCESS_ROOT_LABEL, Messages.PROCESS_ROOT_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_STATE, Messages.PROCESS_STATE_LABEL, Messages.PROCESS_STATE_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_UID, Messages.PROCESS_UID_LABEL, Messages.PROCESS_UID_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_USERNAME, Messages.PROCESS_USERNAME_LABEL, Messages.PROCESS_USERNAME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_UGID, Messages.PROCESS_GID_LABEL, Messages.PROCESS_GID_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_GROUPNAME, Messages.PROCESS_GROUPNAME_LABEL, Messages.PROCESS_GROUPNAME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_PPID, Messages.PROCESS_PPID_LABEL, Messages.PROCESS_PPID_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_PGRP, Messages.PROCESS_PGRP_LABEL, Messages.PROCESS_PGRP_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_TGID, Messages.PROCESS_TGID_LABEL, Messages.PROCESS_TGID_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_TRACERPID, Messages.PROCESS_TRACERPID_LABEL, Messages.PROCESS_TRACERPID_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_VSIZE, Messages.PROCESS_VMSIZE_LABEL, Messages.PROCESS_VMSIZE_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_RSS, Messages.PROCESS_VMRSS_LABEL, Messages.PROCESS_VMRSS_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_SESSION, Messages.PROCESS_SESSION_LABEL, Messages.PROCESS_SESSION_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_TTY, Messages.PROCESS_TTY_LABEL, Messages.PROCESS_TTY_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_FLAGS, Messages.PROCESS_FLAGS_LABEL, Messages.PROCESS_FLAGS_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_MINFLT, Messages.PROCESS_MINFLT_LABEL, Messages.PROCESS_MINFLT_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_CMINFLT, Messages.PROCESS_CMINFLT_LABEL, Messages.PROCESS_CMINFLT_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_MAJFLT, Messages.PROCESS_MAJFLT_LABEL, Messages.PROCESS_MAJFLT_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_CMAJFLT, Messages.PROCESS_CMAJFLT_LABEL, Messages.PROCESS_CMAJFLT_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_UTIME, Messages.PROCESS_UTIME_LABEL, Messages.PROCESS_UTIME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_STIME, Messages.PROCESS_STIME_LABEL, Messages.PROCESS_STIME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_CUTIME, Messages.PROCESS_CUTIME_LABEL, Messages.PROCESS_CUTIME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_CSTIME, Messages.PROCESS_CSTIME_LABEL, Messages.PROCESS_CSTIME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(PROP_PC_UTIME, Messages.PROCESS_PC_UTIME_LABEL, Messages.PROCESS_PC_UTIME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(PROP_PC_STIME, Messages.PROCESS_PC_STIME_LABEL, Messages.PROCESS_PC_STIME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_PRIORITY, Messages.PROCESS_PRIORITY_LABEL, Messages.PROCESS_PRIORITY_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_NICE, Messages.PROCESS_NICE_LABEL, Messages.PROCESS_NICE_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_ITREALVALUE, Messages.PROCESS_ITREALVALUE_LABEL, Messages.PROCESS_ITREALVALUE_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_STARTTIME, Messages.PROCESS_STARTTIME_LABEL, Messages.PROCESS_STARTTIME_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_RLIMIT, Messages.PROCESS_RLIMIT_LABEL, Messages.PROCESS_RLIMIT_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_CODESTART, Messages.PROCESS_CODESTART_LABEL, Messages.PROCESS_CODESTART_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_CODEEND, Messages.PROCESS_CODEEND_LABEL, Messages.PROCESS_CODEEND_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_STACKSTART, Messages.PROCESS_STACKSTART_LABEL, Messages.PROCESS_STACKSTART_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_SIGNALS, Messages.PROCESS_SIGNALS_LABEL, Messages.PROCESS_SIGNALS_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_SIGBLOCK, Messages.PROCESS_SIGBLOCK_LABEL, Messages.PROCESS_SIGBLOCK_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_SIGIGNORE, Messages.PROCESS_SIGIGNORE_LABEL, Messages.PROCESS_SIGIGNORE_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_SIGCATCH, Messages.PROCESS_SIGCATCH_LABEL, Messages.PROCESS_SIGCATCH_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_WCHAN, Messages.PROCESS_WCHAN_LABEL, Messages.PROCESS_WCHAN_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_NSWAP, Messages.PROCESS_NSWAP_LABEL, Messages.PROCESS_NSWAP_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_CNSWAP, Messages.PROCESS_CNSWAP_LABEL, Messages.PROCESS_CNSWAP_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_EXITSIGNAL, Messages.PROCESS_EXITSIGNAL_LABEL, Messages.PROCESS_EXITSIGNAL_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_PROCESSOR, Messages.PROCESS_PROCESSOR_LABEL, Messages.PROCESS_PROCESSOR_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_RTPRIORITY, Messages.PROCESS_RTPRIORITY_LABEL, Messages.PROCESS_RTPRIORITY_TOOLTIP)); + l.add(createSimplePropertyDescriptor(ISysMonitor.PROP_POLICY, Messages.PROCESS_POLICY_LABEL, Messages.PROCESS_POLICY_TOOLTIP)); + + properties = l.toArray(new IPropertyDescriptor[l.size()]); + return properties; + } + + /** + * Returns the current value for the named property. + * @return the current value of the given property + */ + protected Object internalGetPropertyValue(Object key) { + return getPropertyValue(key, true); + } + + /** + * Returns the current value for the named property. + * + * @param property the name or key of the property as named by its property descriptor + * @param formatted indication of whether to return the value in formatted or raw form + * @return the current value of the given property + */ + public Object getPropertyValue(Object property, boolean formatted) { + String name = (String)property; + TCFRemoteProcess process = (TCFRemoteProcess)propertySourceInput; + Object p = process.getProperties().get((String)property); + + if (formatted) { + if (name.equals(ISysMonitor.PROP_VSIZE)) { + return sub(Messages.PROCESS_VMSIZE_VALUE, "&1", Long.toString(process.getVmSizeInKB())); + } + if (name.equals(ISysMonitor.PROP_RSS)) { + return sub(Messages.PROCESS_VMRSS_VALUE, "&1", Long.toString(process.getVmRSSInKB())); + } + if (name.equals(ISysMonitor.PROP_SIGNALS))return formatBitSet(p); + if (name.equals(ISysMonitor.PROP_SIGBLOCK)) return formatBitSet(p); + if (name.equals(ISysMonitor.PROP_SIGCATCH)) return formatBitSet(p); + if (name.equals(ISysMonitor.PROP_SIGIGNORE)) return formatBitSet(p); + if (name.equals(ISysMonitor.PROP_CODESTART)) return formatHex(p); + if (name.equals(ISysMonitor.PROP_CODEEND)) return formatHex(p); + if (name.equals(ISysMonitor.PROP_STACKSTART)) return formatHex(p); + if (name.equals(ISysMonitor.PROP_WCHAN)) return formatHex(p); + if (name.equals(ISysMonitor.PROP_FLAGS)) return formatBitSet(p); + if (name.equals(ISysMonitor.PROP_UTIME)) return formatTime(p); + if (name.equals(ISysMonitor.PROP_STIME)) return formatTime(p); + if (name.equals(ISysMonitor.PROP_CUTIME)) return formatTime(p); + if (name.equals(ISysMonitor.PROP_CSTIME)) return formatTime(p); + if (name.equals(ISysMonitor.PROP_STARTTIME)) return formatTime(p); + if (name.equals(ISysMonitor.PROP_ITREALVALUE)) return formatTime(p); + if (name.equals(PROP_PC_UTIME)) return process.getUserTimePC(); + if (name.equals(PROP_PC_STIME)) return process.getSysTimePC(); + } + + if (p == null) return null; + if (formatted) return p.toString(); + return p; + } + + private String formatTime(Object o) { + if (o instanceof Number) { + BigInteger n = new BigInteger(o.toString()); + BigInteger s[] = n.divideAndRemainder(BigInteger.valueOf(1000)); + BigInteger m[] = s[0].divideAndRemainder(BigInteger.valueOf(60)); + BigInteger h[] = m[0].divideAndRemainder(BigInteger.valueOf(60)); + StringBuffer buf = new StringBuffer(); + if (!h[0].equals(BigInteger.ZERO)) { + buf.append(h[0]); + buf.append("h "); + } + if (buf.length() > 0 || !h[1].equals(BigInteger.ZERO)) { + buf.append(h[1]); + buf.append("m "); + } + buf.append(m[1]); + buf.append('.'); + String ms = s[1].toString(); + buf.append("000".substring(ms.length())); + buf.append(ms); + buf.append('s'); + return buf.toString(); + } + if (o == null) return null; + return o.toString(); + } + + private void formatHex(StringBuffer buf, BigInteger n, int cnt) { + BigInteger m[] = n.divideAndRemainder(BigInteger.valueOf(16)); + if (cnt < 7 || !m[0].equals(BigInteger.ZERO)) { + formatHex(buf, m[0], cnt + 1); + } + int d = m[1].intValue(); + buf.append((char)(d <= 9 ? '0' + d : 'a' + d - 10)); + } + + protected String formatHex(Object o) { + if (o instanceof Number) { + BigInteger n = new BigInteger(o.toString()); + StringBuffer buf = new StringBuffer(); + buf.append("0x"); + formatHex(buf, n, 0); + return buf.toString(); + } + if (o == null) return null; + return o.toString(); + } + + protected String formatBitSet(Object o) { + if (o instanceof Number) { + StringBuffer buf = new StringBuffer(); + long n = ((Number)o).longValue(); + for (int i = 0; i < 64; i++) { + if ((n & (1l << i)) != 0) { + if (buf.length() > 0) buf.append(','); + int i0 = i; + while (i < 63 && (n & (1l << (i + 1))) != 0) i++; + buf.append(i0); + if (i0 != i) { + buf.append(".."); + buf.append(i); + } + } + } + return buf.toString(); + } + if (o == null) return null; + return o.toString(); + } + + protected String formatState(String state) { + return state; + } + + /** + * Return fully qualified name that uniquely identifies this remote object's remote parent within its subsystem + */ + public String getAbsoluteParentName(Object element) { + IRemoteProcess process = (IRemoteProcess) element; + IRemoteProcess parent = process.getParentRemoteProcess(); + if (parent != null) return parent.getAbsolutePath(); + else return "/proc/0"; //$NON-NLS-1$ + } + + /** + * Given a remote object, returns it remote parent object. Eg, given a process, return the process that + * spawned it. + * <p> + * The shell is required in order to set the cursor to a busy state if a remote trip is required. + * + * @return an IRemoteProcess object for the parent + */ + public Object getRemoteParent(Object element, IProgressMonitor monitor) throws Exception { + return ((IRemoteProcess) element).getParentRemoteProcess(); + } + + /** + * Given a remote object, return the unqualified names of the objects contained in that parent. This is + * used for testing for uniqueness on a rename operation, for example. Sometimes, it is not + * enough to just enumerate all the objects in the parent for this purpose, because duplicate + * names are allowed if the types are different, such as on iSeries. In this case return only + * the names which should be used to do name-uniqueness validation on a rename operation. + * + * @return an array of all file and folder names in the parent of the given IRemoteFile object + */ + public String[] getRemoteParentNamesInUse(Object element, IProgressMonitor monitor) throws Exception { + String[] pids = EMPTY_STRING_LIST; + + IRemoteProcess process = (IRemoteProcess) element; + String parentName = "" + process.getPPid(); //$NON-NLS-1$ + if (parentName.equals("-1")) // given a root? //$NON-NLS-1$ + return pids; // not much we can do. Should never happen: you can't rename a root! + + Object[] children = getChildren(process.getParentRemoteProcess(), monitor); + if ((children == null) || (children.length == 0)) + return pids; + + pids = new String[children.length]; + for (int idx = 0; idx < pids.length; idx++) + pids[idx] = "" + ((IRemoteProcess) children[idx]).getPid(); //$NON-NLS-1$ + + return pids; + } + + public String getRemoteSubType(Object element) { + return null; + } + + public String getRemoteType(Object element) { + IRemoteProcess process = (IRemoteProcess) element; + if (process.isRoot()) + return ISystemProcessRemoteTypes.TYPE_ROOT; + else + return ISystemProcessRemoteTypes.TYPE_PROCESS; + } + + public String getRemoteTypeCategory(Object element) { + return ISystemProcessRemoteTypes.TYPECATEGORY; + } + + /** + * Return the subsystem factory id that owns this remote object + * The value must not be translated, so that property pages registered via xml can subset by it. + */ + public String getSubSystemConfigurationId(Object element) { + IRemoteProcess process = (IRemoteProcess) element; + return process.getParentRemoteProcessSubSystem().getSubSystemConfiguration().getId(); + } + + public boolean refreshRemoteObject(Object oldElement, Object newElement) { + return false; + } + + public boolean supportsUserDefinedActions(Object object) { + return false; + } +} diff --git a/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/messages.properties b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/messages.properties new file mode 100644 index 000000000..3b48e7409 --- /dev/null +++ b/plugins/com.windriver.tcf.rse.ui/src/com/windriver/tcf/rse/ui/messages.properties @@ -0,0 +1,103 @@ +SysMonitor_AllProcesses=All Processes +SysMonitor_Process=Process + +PROCESS_ID_LABEL=Context ID +PROCESS_PID_LABEL=Process ID +PROCESS_NAME_LABEL=Executable Name +PROCESS_CWD_LABEL=CWD +PROCESS_ROOT_LABEL=FS Root +PROCESS_UID_LABEL=User ID +PROCESS_USERNAME_LABEL=Username +PROCESS_GID_LABEL=Group ID +PROCESS_GROUPNAME_LABEL=Groupname +PROCESS_PPID_LABEL=Parent PID +PROCESS_PGRP_LABEL=Proucess Group +PROCESS_STATE_LABEL=State +PROCESS_TRACERPID_LABEL=Tracer PID +PROCESS_VMSIZE_LABEL=VM Size +PROCESS_VMRSS_LABEL=VM RSS +PROCESS_SESSION_LABEL=Session +PROCESS_TTY_LABEL=TTY +PROCESS_TGID_LABEL=Task Group ID +PROCESS_FLAGS_LABEL=Flags +PROCESS_MINFLT_LABEL=Minor Faults +PROCESS_CMINFLT_LABEL=Children Minor Faults +PROCESS_MAJFLT_LABEL=Major Faults +PROCESS_CMAJFLT_LABEL=Children Major Faults +PROCESS_UTIME_LABEL=User Time +PROCESS_STIME_LABEL=System Time +PROCESS_CUTIME_LABEL=Children User Time +PROCESS_CSTIME_LABEL=Children System Time +PROCESS_PC_UTIME_LABEL=% User Time +PROCESS_PC_STIME_LABEL=% System Time +PROCESS_PRIORITY_LABEL=Priority +PROCESS_NICE_LABEL=Nice +PROCESS_ITREALVALUE_LABEL=Interval Timer +PROCESS_STARTTIME_LABEL=Start Time +PROCESS_RLIMIT_LABEL=RSS Limit +PROCESS_CODESTART_LABEL=Code Start +PROCESS_CODEEND_LABEL=Code End +PROCESS_STACKSTART_LABEL=Stack Start +PROCESS_SIGNALS_LABEL=Pending Signals +PROCESS_SIGBLOCK_LABEL=Blocked Signals +PROCESS_SIGIGNORE_LABEL=Ignored Signals +PROCESS_SIGCATCH_LABEL=Caught Signals +PROCESS_WCHAN_LABEL=Wait Channel +PROCESS_NSWAP_LABEL=Swaped Pages +PROCESS_CNSWAP_LABEL=Children Swapped Pages +PROCESS_EXITSIGNAL_LABEL=Exit Signal +PROCESS_PROCESSOR_LABEL=Processor +PROCESS_RTPRIORITY_LABEL=Real Time Priority +PROCESS_POLICY_LABEL=Scheduling Policy + +PROCESS_ID_TOOLTIP=TCF context ID of the process +PROCESS_PID_TOOLTIP=The system ID number of the process +PROCESS_NAME_TOOLTIP=The executable associated with the process +PROCESS_CWD_TOOLTIP=Current working directory of the process +PROCESS_ROOT_TOOLTIP=Current file system root of the process +PROCESS_UID_TOOLTIP=The user ID of the owner of the process +PROCESS_USERNAME_TOOLTIP=The username of the owner of the process +PROCESS_GID_TOOLTIP=The group ID of the owner of the process +PROCESS_GROUPNAME_TOOLTIP=The groupname of the owner of the process +PROCESS_PPID_TOOLTIP=The process ID of the parent of the process +PROCESS_PGRP_TOOLTIP=The process group ID +PROCESS_STATE_TOOLTIP=The state in which the process currently is +PROCESS_TRACERPID_TOOLTIP=The tracer process ID of the process +PROCESS_VMSIZE_TOOLTIP=The amount of virtual memory taken up by this process +PROCESS_VMRSS_TOOLTIP=The amount of virtual memory resident set size of this process (actual RAM taken up by the process) +PROCESS_SESSION_TOOLTIP=The session ID of the process +PROCESS_TTY_TOOLTIP=The TTY the process uses +PROCESS_TGID_TOOLTIP=The task group to which process belongs +PROCESS_FLAGS_TOOLTIP=The kernel flags word of the process. +PROCESS_MINFLT_TOOLTIP=The number of minor faults the process has made which have not required loading a memory page from disk. +PROCESS_CMINFLT_TOOLTIP=The number of minor faults that the process's waited-for children have made. +PROCESS_MAJFLT_TOOLTIP=The number of major faults the process has made which have required loading a memory page from disk. +PROCESS_CMAJFLT_TOOLTIP=Children Major Faults +PROCESS_UTIME_TOOLTIP=User Time +PROCESS_STIME_TOOLTIP=System Time +PROCESS_CUTIME_TOOLTIP=Children User Time +PROCESS_CSTIME_TOOLTIP=Children System Time +PROCESS_PC_UTIME_TOOLTIP=% User Time +PROCESS_PC_STIME_TOOLTIP=% System Time +PROCESS_PRIORITY_TOOLTIP=Priority +PROCESS_NICE_TOOLTIP=Nice +PROCESS_ITREALVALUE_TOOLTIP=Interval Timer +PROCESS_STARTTIME_TOOLTIP=Start Time +PROCESS_RLIMIT_TOOLTIP=RSS Limit +PROCESS_CODESTART_TOOLTIP=Code Start +PROCESS_CODEEND_TOOLTIP=Code End +PROCESS_STACKSTART_TOOLTIP=Stack Start +PROCESS_SIGNALS_TOOLTIP=Pending Signals +PROCESS_SIGBLOCK_TOOLTIP=Blocked Signals +PROCESS_SIGIGNORE_TOOLTIP=Ignored Signals +PROCESS_SIGCATCH_TOOLTIP=Caught Signals +PROCESS_WCHAN_TOOLTIP=Wait Channel +PROCESS_NSWAP_TOOLTIP=Swaped Pages +PROCESS_CNSWAP_TOOLTIP=Children Swapped Pages +PROCESS_EXITSIGNAL_TOOLTIP=Exit Signal +PROCESS_PROCESSOR_TOOLTIP=Processor +PROCESS_RTPRIORITY_TOOLTIP=Real Time Priority +PROCESS_POLICY_TOOLTIP=Scheduling Policy + +PROCESS_VMSIZE_VALUE=&1 KB +PROCESS_VMRSS_VALUE=&1 KB
\ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 000000000..ef53380e4 --- /dev/null +++ b/readme.txt @@ -0,0 +1,5 @@ +TCF is Copyright (c) 2007 by Wind River Systems, Inc. +Made available under the Eclipse Public License (EPL) v1.0 +See the file epl-v10.html for details. + +See docs/index.html for documentation on Target Communication Framework
\ No newline at end of file |