Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.cproject1126
-rwxr-xr-x.project81
-rw-r--r--Makefile15
-rw-r--r--Makefile_cygwin.mak16
-rw-r--r--TODO.txt80
-rw-r--r--agent.dsp341
-rw-r--r--agent.dsw29
-rw-r--r--base64.c112
-rw-r--r--base64.h35
-rw-r--r--breakpoints.c964
-rw-r--r--breakpoints.h47
-rw-r--r--channel.c763
-rw-r--r--channel.h70
-rw-r--r--cmdline.c85
-rw-r--r--cmdline.h21
-rw-r--r--config.h43
-rw-r--r--context.c1159
-rw-r--r--context.h136
-rw-r--r--diagnostics.c185
-rw-r--r--diagnostics.h22
-rw-r--r--dwarf.h563
-rw-r--r--dwarfio.c728
-rw-r--r--dwarfio.h77
-rw-r--r--elf.c235
-rw-r--r--elf.h112
-rw-r--r--errors.c58
-rw-r--r--errors.h41
-rw-r--r--events.c194
-rw-r--r--events.h37
-rw-r--r--exceptions.c84
-rw-r--r--exceptions.h54
-rw-r--r--expressions.c854
-rw-r--r--expressions.h47
-rw-r--r--filesystem.c1103
-rw-r--r--filesystem.h24
-rw-r--r--json.c489
-rw-r--r--json.h50
-rw-r--r--linenumbers.c641
-rw-r--r--linenumbers.h27
-rw-r--r--link.h46
-rw-r--r--main.c173
-rw-r--r--mdep.c621
-rw-r--r--mdep.h278
-rw-r--r--memory.c579
-rw-r--r--memory.h25
-rw-r--r--myalloc.c67
-rw-r--r--myalloc.h26
-rw-r--r--processes.c419
-rw-r--r--processes.h29
-rw-r--r--protocol.c205
-rw-r--r--protocol.h61
-rw-r--r--proxy.c22
-rw-r--r--proxy.h23
-rw-r--r--registers.c376
-rw-r--r--registers.h25
-rw-r--r--runctrl.c773
-rw-r--r--runctrl.h41
-rw-r--r--stacktrace.c425
-rw-r--r--stacktrace.h25
-rw-r--r--streams.c25
-rw-r--r--streams.h44
-rw-r--r--symbols.c231
-rw-r--r--symbols.h34
-rw-r--r--sysmon.c657
-rw-r--r--sysmon.h24
-rw-r--r--tcf.h20
-rw-r--r--test.c101
-rw-r--r--test.h23
-rw-r--r--trace.c60
-rw-r--r--trace.h43
70 files changed, 16249 insertions, 0 deletions
diff --git a/.cproject b/.cproject
new file mode 100755
index 00000000..94a4eba5
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,1126 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?>
+
+<cproject>
+<storageModule moduleId="org.eclipse.cdt.core.settings">
+<cconfiguration id="0.586632354">
+<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="0.586632354" moduleId="org.eclipse.cdt.core.settings" name="Default">
+<externalSettings/>
+<extensions>
+<extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+</extensions>
+</storageModule>
+<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+<configuration artifactName="tcf_agent" buildProperties="" description="" id="0.586632354" name="Default" parent="org.eclipse.cdt.build.core.prefbase.cfg">
+<folderInfo id="0.586632354." name="/" resourcePath="">
+<toolChain id="org.eclipse.cdt.build.core.prefbase.toolchain.629729441" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">
+<targetPlatform id="org.eclipse.cdt.build.core.prefbase.toolchain.629729441.1497928954" name=""/>
+<builder buildPath="${workspace_loc:/tcf_agent}" id="org.eclipse.cdt.build.core.settings.default.builder.2122878837" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
+<tool id="org.eclipse.cdt.build.core.settings.holder.libs.2009918965" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/>
+<tool id="org.eclipse.cdt.build.core.settings.holder.287150531" name="Assembly" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.766656885" languageId="org.eclipse.cdt.core.assembly" languageName="Assembly" sourceContentType="org.eclipse.cdt.core.asmSource" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.1130235898" name="UPC" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.392331765" languageId="org.eclipse.cdt.core.parser.upc.upc" languageName="UPC" sourceContentType="org.eclipse.cdt.core.parser.upc.upcSource" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.371530632" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.1910112652" languageId="org.eclipse.cdt.core.g++" languageName="GNU C++" sourceContentType="org.eclipse.cdt.core.cxxSource,org.eclipse.cdt.core.cxxHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.1537638852" name="GNU C" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.710318778" languageId="org.eclipse.cdt.core.gcc" languageName="GNU C" sourceContentType="org.eclipse.cdt.core.cSource,org.eclipse.cdt.core.cHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+</toolChain>
+</folderInfo>
+</configuration>
+</storageModule>
+
+<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
+<storageModule moduleId="scannerConfiguration">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<scannerConfigBuildInfo instanceId="0.586632354">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+<scannerConfigBuildInfo instanceId="0.586632354.1278891061">
+<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="false"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+<scannerConfigBuildInfo instanceId="0.586632354.1278891061.1624802268">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+</storageModule>
+</cconfiguration>
+<cconfiguration id="0.586632354.1278891061">
+<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="0.586632354.1278891061" moduleId="org.eclipse.cdt.core.settings" name="win32 DevStudio">
+<externalSettings/>
+<extensions>
+<extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+</extensions>
+</storageModule>
+<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+<configuration artifactName="tcf_agent" buildProperties="" description="" id="0.586632354.1278891061" name="win32 DevStudio" parent="org.eclipse.cdt.build.core.prefbase.cfg">
+<folderInfo id="0.586632354.1278891061." name="/" resourcePath="">
+<toolChain id="org.eclipse.cdt.build.core.prefbase.toolchain.21355112" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">
+<targetPlatform id="org.eclipse.cdt.build.core.prefbase.toolchain.21355112.397805010" name=""/>
+<builder arguments="" autoBuildTarget="all" buildPath="${workspace_loc:/tcf_agent}" cleanBuildTarget="clean" command="" enableAutoBuild="false" enableCleanBuild="false" enabledIncrementalBuild="false" id="org.eclipse.cdt.build.core.settings.default.builder.405250935" incrementalBuildTarget="all" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelizationNumber="1" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
+<tool id="org.eclipse.cdt.build.core.settings.holder.libs.927042990" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/>
+<tool id="org.eclipse.cdt.build.core.settings.holder.912870361" name="Assembly" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.1307700115" languageId="org.eclipse.cdt.core.assembly" languageName="Assembly" sourceContentType="org.eclipse.cdt.core.asmSource" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.1871552088" name="UPC" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.253767805" languageId="org.eclipse.cdt.core.parser.upc.upc" languageName="UPC" sourceContentType="org.eclipse.cdt.core.parser.upc.upcSource" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.1705508330" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.990630428" languageId="org.eclipse.cdt.core.g++" languageName="GNU C++" sourceContentType="org.eclipse.cdt.core.cxxSource,org.eclipse.cdt.core.cxxHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.392253741" name="GNU C" superClass="org.eclipse.cdt.build.core.settings.holder">
+<option id="org.eclipse.cdt.build.core.settings.holder.symbols.133765132" name="Symbols" superClass="org.eclipse.cdt.build.core.settings.holder.symbols" valueType="definedSymbols">
+<listOptionValue builtIn="false" value="WIN32"/>
+</option>
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.2133262814" languageId="org.eclipse.cdt.core.gcc" languageName="GNU C" sourceContentType="org.eclipse.cdt.core.cSource,org.eclipse.cdt.core.cHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+</toolChain>
+</folderInfo>
+</configuration>
+</storageModule>
+
+<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
+<storageModule moduleId="scannerConfiguration">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<scannerConfigBuildInfo instanceId="0.586632354">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+<scannerConfigBuildInfo instanceId="0.586632354.1278891061">
+<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="false"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+<scannerConfigBuildInfo instanceId="0.586632354.1278891061.1624802268">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+</storageModule>
+</cconfiguration>
+<cconfiguration id="0.586632354.1278891061.1624802268">
+<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="0.586632354.1278891061.1624802268" moduleId="org.eclipse.cdt.core.settings" name="win32 Cygwin">
+<externalSettings/>
+<extensions>
+<extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+</extensions>
+</storageModule>
+<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+<configuration artifactName="tcf_agent" buildProperties="" description="" id="0.586632354.1278891061.1624802268" name="win32 Cygwin" parent="org.eclipse.cdt.build.core.prefbase.cfg">
+<folderInfo id="0.586632354.1278891061.1624802268." name="/" resourcePath="">
+<toolChain id="org.eclipse.cdt.build.core.prefbase.toolchain.817366280" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">
+<targetPlatform id="org.eclipse.cdt.build.core.prefbase.toolchain.817366280.1321601800" name=""/>
+<builder arguments="-fMakefile_cygwin.mak" autoBuildTarget="all" buildPath="${workspace_loc:/tcf_agent}" cleanBuildTarget="clean" command="make" enableAutoBuild="false" enableCleanBuild="true" enabledIncrementalBuild="true" id="org.eclipse.cdt.build.core.settings.default.builder.248686569" incrementalBuildTarget="all" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelizationNumber="1" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
+<tool id="org.eclipse.cdt.build.core.settings.holder.libs.2050088693" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/>
+<tool id="org.eclipse.cdt.build.core.settings.holder.760047734" name="Assembly" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.293829655" languageId="org.eclipse.cdt.core.assembly" languageName="Assembly" sourceContentType="org.eclipse.cdt.core.asmSource" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.1467692607" name="UPC" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.1382477290" languageId="org.eclipse.cdt.core.parser.upc.upc" languageName="UPC" sourceContentType="org.eclipse.cdt.core.parser.upc.upcSource" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.2054278284" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder">
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.825872592" languageId="org.eclipse.cdt.core.g++" languageName="GNU C++" sourceContentType="org.eclipse.cdt.core.cxxSource,org.eclipse.cdt.core.cxxHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+<tool id="org.eclipse.cdt.build.core.settings.holder.60917234" name="GNU C" superClass="org.eclipse.cdt.build.core.settings.holder">
+<option id="org.eclipse.cdt.build.core.settings.holder.symbols.749525598" name="Symbols" superClass="org.eclipse.cdt.build.core.settings.holder.symbols" valueType="definedSymbols">
+<listOptionValue builtIn="false" value="WIN32"/>
+</option>
+<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.1931856410" languageId="org.eclipse.cdt.core.gcc" languageName="GNU C" sourceContentType="org.eclipse.cdt.core.cSource,org.eclipse.cdt.core.cHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
+</tool>
+</toolChain>
+</folderInfo>
+</configuration>
+</storageModule>
+
+<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
+<storageModule moduleId="scannerConfiguration">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<scannerConfigBuildInfo instanceId="0.586632354">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+<scannerConfigBuildInfo instanceId="0.586632354.1278891061">
+<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="false"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+<scannerConfigBuildInfo instanceId="0.586632354.1278891061.1624802268">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+</storageModule>
+</cconfiguration>
+</storageModule>
+<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+<project id="tcf_agent.null.1863878724" name="tcf_agent"/>
+</storageModule>
+</cproject>
diff --git a/.project b/.project
new file mode 100755
index 00000000..ab151381
--- /dev/null
+++ b/.project
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>tcf_agent</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+ <triggers>clean,full,incremental,</triggers>
+ <arguments>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
+ <value>clean</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableCleanBuild</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>?name?</key>
+ <value></value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.append_environment</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.stopOnError</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.buildCommand</key>
+ <value>make</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.contents</key>
+ <value>org.eclipse.cdt.make.core.activeConfigSettings</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.buildLocation</key>
+ <value>${workspace_loc:/tcf_agent}</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableAutoBuild</key>
+ <value>false</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableFullBuild</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.buildArguments</key>
+ <value></value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.fullBuildTarget</key>
+ <value>all</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.autoBuildTarget</key>
+ <value>all</value>
+ </dictionary>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+ <nature>org.eclipse.cdt.core.cnature</nature>
+ </natures>
+</projectDescription>
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..7a4717c4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+CC=gcc
+CFLAGS=-g -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_GNU_SOURCE
+OFILES=$(addsuffix .o,$(basename $(wildcard *.c)))
+HFILES=$(wildcard *.h)
+
+all: agent
+
+agent: $(OFILES)
+ $(CC) $(CFLAGS) -o $@ $(OFILES) -lpthread -lrt -lelf
+
+%.o: %.c $(HFILES)
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+clean:
+ rm -f *.o agent
diff --git a/Makefile_cygwin.mak b/Makefile_cygwin.mak
new file mode 100644
index 00000000..e797e4da
--- /dev/null
+++ b/Makefile_cygwin.mak
@@ -0,0 +1,16 @@
+CC=gcc -mconsole -mwin32 -mno-cygwin
+#CFLAGS=-O2 -DWIN32 -DNDEBUG -D_CONSOLE -D_MBCS
+CFLAGS=-g -DWIN32 -D_DEBUG -D_CONSOLE -D_MBCS
+OFILES=$(addsuffix .o,$(basename $(wildcard *.c)))
+HFILES=$(wildcard *.h)
+
+all: agent
+
+agent: $(OFILES)
+ $(CC) $(CFLAGS) -o $@ $(OFILES) -lkernel32 -luser32 -lshell32 -lWS2_32 -lIphlpapi
+
+%.o: %.c $(HFILES)
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+clean:
+ rm -f *.o agent
diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 00000000..172ebc60
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,80 @@
+ o Shouldn't we separate command parsing from a service implementation?
+
+ o we should use a typedef for addresses
+
+- breakpoints.[ch]
+
+ o We don’t have any way to specify context specific breakpoints yet, right? I think breakpoint
+ should always be associated with a context and since contexts can be execution contexts or containers
+ on different levels it is possible to describe thread specific breakpoints, process specific breakpoints,
+ system global breakpoint and any levels in-between.
+
+- context.c
+
+ o event_pid_stopped() using the PTRACE_EVENT_CLONE to determine if the new context is a thread is not correct. The only
+ way I know of is to look at the Tgid field of /proc/<pid>/status
+
+- runctrl.c
+
+ o command_resume() and command_step_into() share so much code that they could be unified.
+
+ o Sometimes you flush “broadcast_stream.flush(&broadcast_stream);”. What is the logic/rules?
+
+ o After prototype stage, the safe event mechanism needs to have finer granularity so it does not stop more than the process.
+
+ o VxWorks: Need Component Description File for the agent: C:\WindRiver-2.6\vxworks-6.5\target\config\comps\vxWorks
+
+11/1/2007
+
+- It would be really good to put comments on at least the ­­all global functions in headers.
+
+- Complex structures would also be very good to put comments on. For example in breakpoints.c
+
+- Somehow we should make it clear what needs to be done to add another transport layer.
+ Perhaps have a template or a readme file for it.
+
+- breakpoints.c, line 190: if (!bi->ctx->exited && bi->ctx->stopped) {
+
+ o Why is bi->ctx->stopped test needed? It looks like we forget to unpatch the instruction is the target
+ happens to be running when we get to this point since after the if the “planted” flag is unconditionally
+ cleared. Shouldn’t this be an assert instead?
+
+ o Similar problem if context_write_mem() results in error then we clear the planted flag even though we haven’t
+ really unplanted it.
+
+- breakpoints.c, line 231: if (bi->ctx->exited || !bi->ctx->stopped) {
+
+ o What is the inside this used for?
+
+- streams.h:
+
+ o It would be good if the InputStream and OutputStream was designed so characters could be read/written without
+ causing a function call in the normal case. Something similar to stdio getc/putc.
+
+- channel.c in function channels_suspend(): Should we add “assert(!suspended);” to make sure it is not called multiple
+ times since we don’t have a suspend count?
+
+- context.c, line 891 & 928:
+
+ o should say “~(WORD_SIZE-1)” instead of “~3ul”
+
+- processes.c, line 266:
+
+ o We should have an abstraction of the signal number or some way from the client to map between names and numbers
+ so clients don’t have to be target dependent.
+
+- registers.c:
+
+ o Get/set commands does not handle reading multiple registers. Should it?
+
+ o Why are register values returned as strings?
+
+- runctrl.c, line 326
+
+ o Should it not return the error code to the client?
+
+- stacktrace.c
+
+ o line 176: Don’t we need to check code[0] in this case?
+
+ o Line 242: no error check on strtol. Why not used id2pid?
diff --git a/agent.dsp b/agent.dsp
new file mode 100644
index 00000000..862e48bf
--- /dev/null
+++ b/agent.dsp
@@ -0,0 +1,341 @@
+# Microsoft Developer Studio Project File - Name="agent" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=agent - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "agent.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "agent.mak" CFG="agent - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "agent - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "agent - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "agent - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib WS2_32.lib Iphlpapi.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "agent - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib WS2_32.lib Iphlpapi.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "agent - Win32 Release"
+# Name "agent - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\base64.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\base64.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\breakpoints.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\breakpoints.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\channel.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\cmdline.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\cmdline.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\context.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\context.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\diagnostics.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\diagnostics.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dwarf.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dwarfio.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\dwarfio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\elf.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\elf.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\errors.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\errors.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\events.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\events.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\exceptions.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\exceptions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\expressions.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\expressions.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\filesystem.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\filesystem.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\json.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\json.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\linenumbers.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\linenumbers.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\link.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mdep.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mdep.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\memory.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\memory.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\myalloc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\myalloc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\processes.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\processes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\protocol.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\protocol.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\proxy.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\proxy.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\registers.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\registers.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\runctrl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\runctrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\stacktrace.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\stacktrace.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\streams.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\streams.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\symbols.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\symbols.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sysmon.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sysmon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\tcf.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\test.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\test.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TODO.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\trace.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\trace.h
+# End Source File
+# End Target
+# End Project
diff --git a/agent.dsw b/agent.dsw
new file mode 100644
index 00000000..18f18f79
--- /dev/null
+++ b/agent.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "agent"=.\agent.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/base64.c b/base64.c
new file mode 100644
index 00000000..e6e5f982
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements BASE64 encoding and decoding of binary data.
+ * BASE64 encoding is adapted from RFC 1421, with one change:
+ * BASE64 eliminates the "*" mechanism for embedded clear text.
+ * Also TCF version of the encoding does not allow characters outside of the BASE64 alphabet.
+ */
+
+#if _WRS_KERNEL
+# include <vxWorks.h>
+#endif
+#include <assert.h>
+#include "base64.h"
+#include "exceptions.h"
+#include "errors.h"
+
+static const 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', '+', '/'
+};
+
+static const int 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
+};
+
+int write_base64(OutputStream * out, char * buf0, int len) {
+ int pos = 0;
+ unsigned char * buf = (unsigned char *)buf0;
+
+ while (pos < len) {
+ int byte0 = buf[pos++];
+ out->write(out, int2char[byte0 >> 2]);
+ if (pos == len) {
+ out->write(out, int2char[(byte0 << 4) & 0x3f]);
+ out->write(out, '=');
+ out->write(out, '=');
+ }
+ else {
+ int byte1 = buf[pos++];
+ out->write(out, int2char[(byte0 << 4) & 0x3f | (byte1 >> 4)]);
+ if (pos == len) {
+ out->write(out, int2char[(byte1 << 2) & 0x3f]);
+ out->write(out, '=');
+ }
+ else {
+ int byte2 = buf[pos++];
+ out->write(out, int2char[(byte1 << 2) & 0x3f | (byte2 >> 6)]);
+ out->write(out, int2char[byte2 & 0x3f]);
+ }
+ }
+ }
+ assert(pos == len);
+ return ((len + 2) / 3) * 4;
+}
+
+int read_base64(InputStream * inp, char * buf, int buf_size) {
+ int pos = 0;
+ int ch_max = sizeof(char2int) / sizeof(int);
+
+ while (pos + 3 <= buf_size) {
+ int n0, n1, n2, n3;
+ int ch0, ch1, ch2, ch3;
+
+ ch0 = inp->peek(inp);
+ if (ch0 < 0 || ch0 >= ch_max || (n0 = char2int[ch0]) < 0) break;
+ inp->read(inp);
+ ch1 = inp->read(inp);
+ ch2 = inp->read(inp);
+ ch3 = inp->read(inp);
+ if (ch1 < 0 || ch1 >= ch_max || (n1 = char2int[ch1]) < 0) exception(ERR_BASE64);
+ buf[pos++] = (char)((n0 << 2) | (n1 >> 4));
+ if (ch2 == '=') break;
+ if (ch2 < 0 || ch2 >= ch_max || (n2 = char2int[ch2]) < 0) exception(ERR_BASE64);
+ buf[pos++] = (char)((n1 << 4) | (n2 >> 2));
+ if (ch3 == '=') break;
+ if (ch3 < 0 || ch3 >= ch_max || (n3 = char2int[ch3]) < 0) exception(ERR_BASE64);
+ buf[pos++] = (char)((n2 << 6) | n3);
+ }
+ return pos;
+}
+
diff --git a/base64.h b/base64.h
new file mode 100644
index 00000000..6df638a5
--- /dev/null
+++ b/base64.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * This module implements BASE64 encoding and decoding of binary data.
+ * BASE64 encoding is adapted from RFC 1421, with one change:
+ * BASE64 eliminates the "*" mechanism for embedded clear text.
+ * Also TCF version of the encoding does not allow characters outside of the BASE64 alphabet.
+ */
+
+#ifndef D_base64
+#define D_base64
+
+#include "streams.h"
+
+/*
+ * Write BASE64 encoded array of bytes to output stream.
+ */
+extern int write_base64(OutputStream * out, char * buf, int len);
+
+/*
+ * Read BASE64 encoded array of bytes from input stream.
+ * Returns number of decoded bytes.
+ */
+extern int read_base64(InputStream * inp, char * buf, int buf_size);
+
+#endif
diff --git a/breakpoints.c b/breakpoints.c
new file mode 100644
index 00000000..94652100
--- /dev/null
+++ b/breakpoints.c
@@ -0,0 +1,964 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements Breakpoints service.
+ * The service maintains a list of breakpoints.
+ * Each breakpoint consists of one or more conditions that determine
+ * when a program's execution should be interrupted.
+ */
+
+#include "config.h"
+#if SERVICE_Breakpoints
+
+// TODO: breakpoint status reports
+// TODO: replant breakpoints when shared lib is loaded or unloaded
+
+#if defined(_WRS_KERNEL)
+# include <vxWorks.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+#include "breakpoints.h"
+#include "expressions.h"
+#include "channel.h"
+#include "protocol.h"
+#include "errors.h"
+#include "trace.h"
+#include "runctrl.h"
+#include "context.h"
+#include "myalloc.h"
+#include "exceptions.h"
+#include "symbols.h"
+#include "json.h"
+#include "link.h"
+
+#ifdef BREAK_INST
+# undef BREAK_INST
+#endif
+
+#if defined(_WRS_KERNEL)
+
+#include <private/vxdbgLibP.h>
+
+#elif defined(__i386__)
+
+static unsigned char BREAK_INST[] = { 0xcc }; /* breakpoint instruction */
+#define BREAK_SIZE 1 /* breakpoint instruction size */
+
+#else
+
+#error "Unknown CPU"
+
+#endif
+
+typedef struct BreakpointRef BreakpointRef;
+typedef struct BreakpointInfo BreakpointInfo;
+typedef struct BreakInstruction BreakInstruction;
+
+struct BreakpointRef {
+ LINK link_inp;
+ LINK link_bp;
+ InputStream * inp;
+ BreakpointInfo * bp;
+};
+
+struct BreakpointInfo {
+ LINK link_all;
+ LINK link_id;
+ LINK refs;
+ char id[64];
+ int enabled;
+ int unsupported;
+ int planted;
+ int deleted;
+ int error;
+ char * err_msg;
+ char * address;
+ char * condition;
+};
+
+struct BreakInstruction {
+ LINK link_all;
+ LINK link_adr;
+ Context * ctx;
+ int ctx_cnt;
+ unsigned long address;
+#if defined(_WRS_KERNEL)
+ VXDBG_CTX vxdbg_ctx;
+ VXDBG_BP_ID vxdbg_id;
+#else
+ char saved_code[BREAK_SIZE];
+#endif
+ int error;
+ int skip;
+ BreakpointInfo ** refs;
+ int ref_size;
+ int ref_cnt;
+ int planted;
+};
+
+static const char * BREAKPOINTS = "Breakpoints";
+
+#define ADDR2INSTR_HASH_SIZE 1023
+#define addr2instr_hash(addr) (((addr) + ((addr) >> 8)) % ADDR2INSTR_HASH_SIZE)
+
+#define link_all2bi(A) ((BreakInstruction *)((char *)(A) - (int)&((BreakInstruction *)0)->link_all))
+#define link_adr2bi(A) ((BreakInstruction *)((char *)(A) - (int)&((BreakInstruction *)0)->link_adr))
+
+#define ID2BP_HASH_SIZE 1023
+
+#define link_all2bp(A) ((BreakpointInfo *)((char *)(A) - (int)&((BreakpointInfo *)0)->link_all))
+#define link_id2bp(A) ((BreakpointInfo *)((char *)(A) - (int)&((BreakpointInfo *)0)->link_id))
+
+#define INP2BR_HASH_SIZE 127
+
+#define link_inp2br(A) ((BreakpointRef *)((char *)(A) - (int)&((BreakpointRef *)0)->link_inp))
+#define link_bp2br(A) ((BreakpointRef *)((char *)(A) - (int)&((BreakpointRef *)0)->link_bp))
+
+static LINK breakpoints;
+static LINK id2bp[ID2BP_HASH_SIZE];
+
+static LINK instructions;
+static LINK addr2instr[ADDR2INSTR_HASH_SIZE];
+
+static LINK inp2br[INP2BR_HASH_SIZE];
+
+static int replanting = 0;
+
+static Context * expression_context = NULL;
+static int address_expression_identifier(char * name, Value * v);
+static int condition_expression_identifier(char * name, Value * v);
+static ExpressionContext bp_address_ctx = { address_expression_identifier, NULL };
+static ExpressionContext bp_condition_ctx = { condition_expression_identifier, NULL };
+
+static int id2bp_hash(char * id) {
+ unsigned hash = 0;
+ while (*id) hash = (hash >> 16) + hash + (unsigned char)*id++;
+ return hash % ID2BP_HASH_SIZE;
+}
+
+static void plant_instruction(BreakInstruction * bi) {
+ assert(!bi->planted);
+#if defined(_WRS_KERNEL)
+ bi->vxdbg_ctx.ctxId = bi->ctx_cnt == 1 ? bi->ctx->pid : 0;
+ bi->vxdbg_ctx.ctxId = 0;
+ bi->vxdbg_ctx.ctxType = VXDBG_CTX_TASK;
+ if (vxdbgBpAdd(vxdbg_clnt_id,
+ &bi->vxdbg_ctx, 0, BP_ACTION_STOP | BP_ACTION_NOTIFY,
+ 0, 0, (INSTR *)bi->address, 0, 0, &bi->vxdbg_id) != OK) {
+ bi->error = errno;
+ assert(bi->error != 0);
+ }
+#else
+ if (context_read_mem(bi->ctx, bi->address, bi->saved_code, BREAK_SIZE) < 0) {
+ bi->error = errno;
+ }
+ else if (context_write_mem(bi->ctx, bi->address, &BREAK_INST, BREAK_SIZE) < 0) {
+ bi->error = errno;
+ }
+#endif
+ bi->planted = bi->error == 0;
+}
+
+static int verify_instruction(BreakInstruction * bi) {
+ assert(bi->planted);
+#if defined(_WRS_KERNEL)
+ return bi->vxdbg_ctx.ctxId == (bi->ctx_cnt == 1 ? bi->ctx->pid : 0) &&
+ bi->vxdbg_ctx.ctxType == VXDBG_CTX_TASK;
+#else
+ return 1;
+#endif
+}
+
+static void remove_instruction(BreakInstruction * bi) {
+ assert(bi->planted);
+ assert(!bi->error);
+#ifdef _WRS_KERNEL
+ {
+ VXDBG_BP_DEL_INFO info;
+ memset(&info, 0, sizeof(info));
+ info.pClnt = vxdbg_clnt_id;
+ info.type = BP_BY_ID_DELETE;
+ info.info.id.bpId = bi->vxdbg_id;
+ if (vxdbgBpDelete(info) != OK) {
+ bi->error = errno;
+ assert(bi->error != 0);
+ }
+ }
+#else
+ if (!bi->ctx->exited && bi->ctx->stopped) {
+ if (context_write_mem(bi->ctx, bi->address, bi->saved_code, BREAK_SIZE) < 0) {
+ bi->error = errno;
+ }
+ }
+#endif
+ bi->planted = 0;
+}
+
+static BreakInstruction * add_instruction(Context * ctx, unsigned long address) {
+ int hash = addr2instr_hash(address);
+ BreakInstruction * bi = (BreakInstruction *)loc_alloc_zero(sizeof(BreakInstruction));
+ list_add_last(&bi->link_all, &instructions);
+ list_add_last(&bi->link_adr, addr2instr + hash);
+ context_lock(ctx);
+ bi->ctx = ctx;
+ bi->address = address;
+ return bi;
+}
+
+static void clear_instruction_refs(void) {
+ LINK * l = instructions.next;
+ while (l != &instructions) {
+ BreakInstruction * bi = link_all2bi(l);
+ bi->ctx_cnt = 1;
+ bi->ref_cnt = 0;
+ l = l->next;
+ }
+}
+
+static void delete_unused_instructions(void) {
+ LINK * l = instructions.next;
+ while (l != &instructions) {
+ BreakInstruction * bi = link_all2bi(l);
+ l = l->next;
+ if (bi->skip) continue;
+ if (bi->ref_cnt == 0) {
+ list_remove(&bi->link_all);
+ list_remove(&bi->link_adr);
+ if (bi->planted) {
+ if (bi->ctx->exited || !bi->ctx->stopped) {
+ LINK * qp = context_root.next;
+ while (qp != &context_root) {
+ Context * ctx = ctxl2ctxp(qp);
+ qp = qp->next;
+ if (ctx->mem == bi->ctx->mem && !ctx->exited && ctx->stopped) {
+ assert(bi->ctx != ctx);
+ context_unlock(bi->ctx);
+ context_lock(ctx);
+ bi->ctx = ctx;
+ break;
+ }
+ }
+ }
+ remove_instruction(bi);
+ }
+ context_unlock(bi->ctx);
+ loc_free(bi->refs);
+ loc_free(bi);
+ }
+ else if (!bi->planted) {
+ plant_instruction(bi);
+ }
+ else if (!verify_instruction(bi)) {
+ remove_instruction(bi);
+ plant_instruction(bi);
+ }
+ }
+}
+
+static BreakInstruction * find_instruction(Context * ctx, unsigned long address) {
+ int hash = addr2instr_hash(address);
+ LINK * l = addr2instr[hash].next;
+ assert(!ctx->exited && ctx->stopped);
+ while (l != addr2instr + hash) {
+ BreakInstruction * bi = link_adr2bi(l);
+ l = l->next;
+ if (bi->ctx->mem == ctx->mem && bi->address == address) {
+ if (bi->ctx->exited || !bi->ctx->stopped) {
+ assert(bi->ctx != ctx);
+ context_unlock(bi->ctx);
+ context_lock(ctx);
+ bi->ctx = ctx;
+ }
+ return bi;
+ }
+ }
+ return NULL;
+}
+
+static int address_expression_identifier(char * name, Value * v) {
+ Symbol sym;
+ if (v == NULL) return 0;
+ memset(v, 0, sizeof(Value));
+ if (expression_context == NULL) {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ if (strcmp(name, "$thread") == 0) {
+ string_value(v, thread_id(expression_context));
+ return 0;
+ }
+ if (find_symbol(expression_context, name, &sym) < 0) {
+ if (errno != ERR_SYM_NOT_FOUND) return -1;
+ }
+ else {
+ v->type = VALUE_UNS;
+ v->value = sym.value;
+ return 0;
+ }
+ errno = ERR_SYM_NOT_FOUND;
+ return -1;
+}
+
+static void address_expression_error(BreakpointInfo * bp, char * msg) {
+ // TODO: per-context address expression error report
+ int size;
+ if (bp->error) return;
+ bp->error = errno;
+ if (msg == NULL) msg = get_expression_error_msg();
+ assert(bp->err_msg == NULL);
+ size = strlen(msg) + strlen(bp->address) + 64;
+ bp->err_msg = loc_alloc(size);
+ snprintf(bp->err_msg, size, "Invalid breakpoint address '%s': %s", bp->address, msg);
+}
+
+static void plant_breakpoint(BreakpointInfo * bp) {
+ LINK * qp;
+ char * p = NULL;
+ Value v;
+ int context_sensitive = 0;
+
+ assert(!bp->planted);
+ assert(bp->enabled);
+ bp->error = 0;
+ if (bp->err_msg != NULL) loc_free(bp->err_msg);
+
+ if (bp->address == NULL) {
+ bp->error = ERR_INV_EXPRESSION;
+ trace(LOG_ALWAYS, "No breakpoint address");
+ return;
+ }
+ expression_context = NULL;
+ if (evaluate_expression(&bp_address_ctx, bp->address, &v) < 0) {
+ if (errno != ERR_INV_CONTEXT) {
+ address_expression_error(bp, NULL);
+ trace(LOG_ALWAYS, "Error: %s", bp->err_msg);
+ return;
+ }
+ context_sensitive = 1;
+ }
+ if (!context_sensitive && v.type != VALUE_INT && v.type != VALUE_UNS) {
+ errno = ERR_INV_EXPRESSION;
+ address_expression_error(bp, "Must be integer number");
+ trace(LOG_ALWAYS, "Error: %s", bp->err_msg);
+ return;
+ }
+
+ for (qp = context_root.next; qp != &context_root; qp = qp->next) {
+ BreakInstruction * bi = NULL;
+ Context * ctx = ctxl2ctxp(qp);
+
+ if (ctx->exited || ctx->exiting) continue;
+ assert(ctx->stopped);
+ if (context_sensitive) {
+ expression_context = ctx;
+ if (evaluate_expression(&bp_address_ctx, bp->address, &v) < 0) {
+ address_expression_error(bp, NULL);
+ trace(LOG_ALWAYS, "Error: %s", bp->err_msg);
+ continue;
+ }
+ if (v.type != VALUE_INT && v.type != VALUE_UNS) {
+ errno = ERR_INV_EXPRESSION;
+ address_expression_error(bp, "Must be integer number");
+ continue;
+ }
+ }
+ if (bp->condition != NULL) {
+ /* Optimize away the breakpoint if condition is always false for given context */
+ Value c;
+ expression_context = ctx;
+ if (evaluate_expression(&bp_address_ctx, bp->condition, &c) == 0) {
+ switch (c.type) {
+ case VALUE_INT:
+ case VALUE_UNS:
+ if (c.value == 0) continue;
+ break;
+ case VALUE_STR:
+ if (c.str == NULL) continue;
+ break;
+ }
+ }
+ }
+ bi = find_instruction(ctx, v.value);
+ if (bi == NULL) {
+ bi = add_instruction(ctx, v.value);
+ }
+ else if (bp->planted) {
+ int i = 0;
+ while (i < bi->ref_cnt && bi->refs[i] != bp) i++;
+ if (i < bi->ref_cnt) continue;
+ }
+ if (bi->ref_cnt >= bi->ref_size) {
+ bi->ref_size = bi->ref_size == 0 ? 8 : bi->ref_size * 2;
+ bi->refs = (BreakpointInfo **)loc_realloc(bi->refs, sizeof(BreakpointInfo *) * bi->ref_size);
+ }
+ bi->refs[bi->ref_cnt++] = bp;
+ if (bi->ctx != ctx) bi->ctx_cnt++;
+ if (bi->error) {
+ if (!bp->error) bp->error = bi->error;
+ }
+ else {
+ bp->planted = 1;
+ }
+ }
+ if (bp->planted) bp->error = 0;
+}
+
+static void event_replant_breakpoints(void *arg) {
+ LINK * l = breakpoints.next;
+ replanting = 0;
+ clear_instruction_refs();
+ while (l != &breakpoints) {
+ BreakpointInfo * bp = link_all2bp(l);
+ l = l->next;
+ if (bp->deleted) {
+ list_remove(&bp->link_all);
+ list_remove(&bp->link_id);
+ loc_free(bp->err_msg);
+ loc_free(bp->address);
+ loc_free(bp->condition);
+ loc_free(bp);
+ continue;
+ }
+ bp->planted = 0;
+ if (bp->enabled && !bp->unsupported) {
+ plant_breakpoint(bp);
+ }
+ }
+ delete_unused_instructions();
+}
+
+static void replant_breakpoints(void) {
+ if (list_is_empty(&breakpoints) && list_is_empty(&instructions)) return;
+ if (replanting) return;
+ replanting = 1;
+ post_safe_event(event_replant_breakpoints, NULL);
+}
+
+static int str_equ(char * x, char * y) {
+ if (x == y) return 1;
+ if (x == NULL) return 0;
+ if (y == NULL) return 0;
+ return strcmp(x, y) == 0;
+}
+
+static int copy_breakpoint_info(BreakpointInfo * dst, BreakpointInfo * src) {
+ int res = 0;
+ if (strcmp(dst->id, src->id) != 0) {
+ strcpy(dst->id, src->id);
+ res = 1;
+ }
+ if (dst->enabled != src->enabled) {
+ dst->enabled = src->enabled;
+ res = 1;
+ }
+ if (dst->unsupported != src->unsupported) {
+ dst->unsupported = src->unsupported;
+ res = 1;
+ }
+ if (!str_equ(dst->address, src->address)) {
+ loc_free(dst->address);
+ dst->address = src->address;
+ res = 1;
+ }
+ else {
+ loc_free(src->address);
+ }
+ src->address = NULL;
+ if (!str_equ(dst->condition, src->condition)) {
+ loc_free(dst->condition);
+ dst->condition = src->condition;
+ res = 1;
+ }
+ else {
+ loc_free(src->condition);
+ }
+ src->condition = NULL;
+ return res;
+}
+
+static BreakpointInfo * find_breakpoint(char * id) {
+ int hash = id2bp_hash(id);
+ LINK * l = id2bp[hash].next;
+ while (l != id2bp + hash) {
+ BreakpointInfo * bp = link_id2bp(l);
+ l = l->next;
+ if (strcmp(bp->id, id) == 0) return bp;
+ }
+ return NULL;
+}
+
+static BreakpointRef * find_breakpoint_ref(BreakpointInfo * bp, InputStream * inp) {
+ LINK * l;
+ if (bp == NULL) return NULL;
+ l = bp->refs.next;
+ while (l != &bp->refs) {
+ BreakpointRef * br = link_bp2br(l);
+ assert(br->bp == bp);
+ if (br->inp == inp) return br;
+ l = l->next;
+ }
+ return NULL;
+}
+
+static void add_breakpoint(InputStream * inp, BreakpointInfo * bp) {
+ BreakpointRef * r = NULL;
+ BreakpointInfo * i = NULL;
+ int chng = 0;
+ i = find_breakpoint(bp->id);
+ if (i == NULL) {
+ int hash = id2bp_hash(bp->id);
+ i = (BreakpointInfo *)loc_alloc_zero(sizeof(BreakpointInfo));
+ list_init(&i->refs);
+ list_add_last(&i->link_all, &breakpoints);
+ list_add_last(&i->link_id, id2bp + hash);
+ }
+ chng = copy_breakpoint_info(i, bp);
+ if (i->deleted) {
+ i->deleted = 0;
+ chng = 1;
+ }
+ r = find_breakpoint_ref(i, inp);
+ if (r == NULL) {
+ int inp_hash = (int)inp / 16 % INP2BR_HASH_SIZE;
+ r = (BreakpointRef *)loc_alloc_zero(sizeof(BreakpointRef));
+ list_add_last(&r->link_inp, inp2br + inp_hash);
+ list_add_last(&r->link_bp, &i->refs);
+ r->inp = inp;
+ r->bp = i;
+ }
+ else {
+ assert(r->bp == i);
+ assert(!list_is_empty(&i->refs));
+ }
+ if (chng) {
+ if (i->planted || i->enabled && !i->unsupported) replant_breakpoints();
+ }
+}
+
+static void remove_breakpoint(BreakpointInfo * bp) {
+ assert(list_is_empty(&bp->refs));
+ if (bp->planted) {
+ bp->deleted = 1;
+ replant_breakpoints();
+ }
+ else {
+ list_remove(&bp->link_all);
+ list_remove(&bp->link_id);
+ loc_free(bp->address);
+ loc_free(bp->condition);
+ loc_free(bp);
+ }
+}
+
+static void remove_ref(BreakpointRef * br) {
+ BreakpointInfo * bp = br->bp;
+ list_remove(&br->link_inp);
+ list_remove(&br->link_bp);
+ loc_free(br);
+ if (list_is_empty(&bp->refs)) remove_breakpoint(bp);
+}
+
+static void delete_breakpoint_refs(InputStream * inp) {
+ int hash = (int)inp / 16 % INP2BR_HASH_SIZE;
+ LINK * l = inp2br[hash].next;
+ while (l != &inp2br[hash]) {
+ BreakpointRef * br = link_inp2br(l);
+ l = l->next;
+ if (br->inp == inp) remove_ref(br);
+ }
+}
+
+static void read_breakpoint_properties(InputStream * inp, BreakpointInfo * bp) {
+ memset(bp, 0, sizeof(BreakpointInfo));
+ if (inp->read(inp) != '{') exception(ERR_JSON_SYNTAX);
+ if (inp->peek(inp) == '}') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ int ch;
+ char name[256];
+ json_read_string(inp, name, sizeof(name));
+ if (inp->read(inp) != ':') exception(ERR_JSON_SYNTAX);
+ if (strcmp(name, "ID") == 0) {
+ json_read_string(inp, bp->id, sizeof(bp->id));
+ }
+ else if (strcmp(name, "Address") == 0) {
+ bp->address = json_read_alloc_string(inp);
+ }
+ else if (strcmp(name, "Condition") == 0) {
+ bp->condition = json_read_alloc_string(inp);
+ }
+ else if (strcmp(name, "Enabled") == 0) {
+ bp->enabled = json_read_boolean(inp);
+ }
+ else {
+ bp->unsupported = 1;
+ json_skip_object(inp);
+ }
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == '}') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+}
+
+static void command_ini_bps(char * token, InputStream * inp, OutputStream * out) {
+ delete_breakpoint_refs(inp);
+ if (inp->read(inp) != '[') exception(ERR_PROTOCOL);
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ int ch;
+ BreakpointInfo bp;
+ read_breakpoint_properties(inp, &bp);
+ add_breakpoint(inp, &bp);
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_bp_ids(char * token, InputStream * inp, OutputStream * out) {
+ // TODO: implement command_get_bp_ids()
+ exception(ERR_PROTOCOL);
+}
+
+static void command_get_properties(char * token, InputStream * inp, OutputStream * out) {
+ // TODO: implement command_get_properties()
+ exception(ERR_PROTOCOL);
+}
+
+static void command_get_status(char * token, InputStream * inp, OutputStream * out) {
+ // TODO: implement command_get_status()
+ exception(ERR_PROTOCOL);
+}
+
+static void command_bp_add(char * token, InputStream * inp, OutputStream * out) {
+ BreakpointInfo bp;
+ read_breakpoint_properties(inp, &bp);
+ add_breakpoint(inp, &bp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_bp_change(char * token, InputStream * inp, OutputStream * out) {
+ BreakpointInfo bp;
+ BreakpointInfo * p;
+ read_breakpoint_properties(inp, &bp);
+ p = find_breakpoint(bp.id);
+ if (p != NULL && copy_breakpoint_info(p, &bp)) {
+ if (p->planted || p->enabled && !p->unsupported) replant_breakpoints();
+ }
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_bp_enable(char * token, InputStream * inp, OutputStream * out) {
+ if (inp->read(inp) != '[') exception(ERR_PROTOCOL);
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ int ch;
+ char id[256];
+ BreakpointInfo * bp;
+ json_read_string(inp, id, sizeof(id));
+ bp = find_breakpoint(id);
+ if (bp != NULL && !bp->enabled) {
+ bp->enabled = 1;
+ if (!bp->deleted && !bp->unsupported) replant_breakpoints();
+ }
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_bp_disable(char * token, InputStream * inp, OutputStream * out) {
+ if (inp->read(inp) != '[') exception(ERR_PROTOCOL);
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ int ch;
+ char id[256];
+ BreakpointInfo * bp;
+ json_read_string(inp, id, sizeof(id));
+ bp = find_breakpoint(id);
+ if (bp != NULL && bp->enabled) {
+ bp->enabled = 0;
+ if (bp->planted) replant_breakpoints();
+ }
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_bp_remove(char * token, InputStream * inp, OutputStream * out) {
+ if (inp->read(inp) != '[') exception(ERR_PROTOCOL);
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ int ch;
+ char id[256];
+ BreakpointRef * br;
+ json_read_string(inp, id, sizeof(id));
+ br = find_breakpoint_ref(find_breakpoint(id), inp);
+ if (br != NULL) remove_ref(br);
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+int is_stopped_by_breakpoint(Context * ctx) {
+ BreakInstruction * bi;
+
+ assert(!ctx->exited);
+ assert(ctx->stopped);
+#ifdef _WRS_KERNEL
+ return ctx->stopped_by_bp;
+#else
+ if (ctx->signal != SIGTRAP) return 0;
+ if (ctx->event != 0) return 0;
+ if (ctx->regs_error) return 0;
+ if (ctx->stopped_by_bp) return 1;
+ bi = find_instruction(ctx, get_regs_PC(ctx->regs) - BREAK_SIZE);
+ if (bi == NULL || bi->skip || bi->error) return 0;
+ set_regs_PC(ctx->regs, get_regs_PC(ctx->regs) - BREAK_SIZE);
+ ctx->regs_dirty = 1;
+ ctx->stopped_by_bp = 1;
+ return 1;
+#endif
+}
+
+static int condition_expression_identifier(char * name, Value * v) {
+ return address_expression_identifier(name, v);
+}
+
+int evaluate_breakpoint_condition(Context * ctx) {
+ int i;
+ BreakInstruction * bi = find_instruction(ctx, get_regs_PC(ctx->regs));
+ if (bi == NULL) return 0;
+ expression_context = ctx;
+ for (i = 0; i < bi->ref_cnt; i++) {
+ Value v;
+ BreakpointInfo * bp = bi->refs[i];
+ assert(bp->planted);
+ assert(bp->error == 0);
+ if (bp->deleted) continue;
+ if (bp->unsupported) continue;
+ if (!bp->enabled) continue;
+ if (bp->condition == NULL) return 1;
+ if (evaluate_expression(&bp_condition_ctx, bp->condition, &v) < 0) {
+ trace(LOG_ALWAYS, "%s: %s", get_expression_error_msg(), bp->condition);
+ return 1;
+ }
+ switch (v.type) {
+ case VALUE_INT:
+ case VALUE_UNS:
+ if (v.value) return 1;
+ break;
+ case VALUE_STR:
+ if (v.str != NULL) return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+#ifndef _WRS_KERNEL
+
+static void safe_restore_breakpoint(void * arg) {
+ SkipBreakpointInfo * sb = (SkipBreakpointInfo *)arg;
+ BreakInstruction * bi = find_instruction(sb->ctx, sb->address);
+
+ assert(sb->ctx->regs_error || get_regs_PC(sb->ctx->regs) != sb->address);
+ if (bi != NULL && bi->skip) {
+ assert(bi->error == 0);
+ bi->skip = 0;
+ plant_instruction(bi);
+ }
+ if (sb->done) sb->done(sb);
+ if (sb->out) stream_unlock(sb->out);
+ context_unlock(sb->ctx);
+ loc_free(sb);
+}
+
+static void safe_skip_breakpoint(void * arg) {
+ SkipBreakpointInfo * sb = (SkipBreakpointInfo *)arg;
+
+ assert(!sb->ctx->exited);
+ assert(sb->ctx->stopped);
+ assert(!sb->ctx->intercepted);
+ assert(!sb->ctx->regs_error);
+ assert(sb->address == get_regs_PC(sb->ctx->regs));
+
+ if (sb->error == 0) {
+ BreakInstruction * bi = find_instruction(sb->ctx, sb->address);
+ if (bi != NULL && !bi->skip) {
+ if (bi->error) {
+ sb->error = bi->error;
+ }
+ else {
+ remove_instruction(bi);
+ bi->skip = 1;
+ }
+ }
+ }
+ if (sb->error == 0) {
+ post_safe_event(safe_restore_breakpoint, sb);
+ if (context_single_step(sb->ctx) < 0) {
+ sb->error = errno;
+ }
+ else if (sb->pending_intercept) {
+ sb->ctx->pending_intercept = 1;
+ }
+ }
+ else {
+ if (sb->done) sb->done(sb);
+ if (sb->out) stream_unlock(sb->out);
+ context_unlock(sb->ctx);
+ loc_free(sb);
+ }
+}
+
+#endif
+
+/*
+ * When a context is stopped by breakpoint, it is necessary to disable
+ * the breakpoint temporarily before the context can be resumed.
+ * This function function removes break instruction, then does single step
+ * over breakpoint location, then restores break intruction.
+ * Return: NULL if it is OK to resume context from current state,
+ * SkipBreakpointInfo pointer if context needs to step over a breakpoint.
+ */
+SkipBreakpointInfo * skip_breakpoint(Context * ctx) {
+ BreakInstruction * bi;
+ SkipBreakpointInfo * sb;
+
+ assert(!ctx->exited);
+ assert(ctx->stopped);
+
+#ifdef _WRS_KERNEL
+ /* VxWork debug library can skip breakpoint when neccesary, no code is needed here */
+ return NULL;
+#else
+ if (ctx->exited || ctx->exiting) return NULL;
+ assert(!ctx->regs_error);
+ bi = find_instruction(ctx, get_regs_PC(ctx->regs));
+ if (bi == NULL || bi->error) return NULL;
+ assert(!bi->skip);
+ sb = (SkipBreakpointInfo *)loc_alloc_zero(sizeof(SkipBreakpointInfo));
+ context_lock(ctx);
+ sb->ctx = ctx;
+ sb->address = get_regs_PC(ctx->regs);
+ post_safe_event(safe_skip_breakpoint, sb);
+ return sb;
+#endif
+}
+
+static void event_context_created_or_exited(Context * ctx) {
+ if (ctx->parent == NULL) replant_breakpoints();
+}
+
+static void channel_close_listener(InputStream * inp, OutputStream * out) {
+ delete_breakpoint_refs(inp);
+}
+
+void ini_breakpoints_service(void) {
+ int i;
+ static ContextEventListener listener = {
+ event_context_created_or_exited,
+ event_context_created_or_exited,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ add_context_event_listener(&listener);
+ list_init(&breakpoints);
+ list_init(&instructions);
+ for (i = 0; i < ADDR2INSTR_HASH_SIZE; i++) list_init(addr2instr + i);
+ for (i = 0; i < ID2BP_HASH_SIZE; i++) list_init(id2bp + i);
+ for (i = 0; i < INP2BR_HASH_SIZE; i++) list_init(inp2br + i);
+ add_channel_close_listener(channel_close_listener);
+ add_command_handler(BREAKPOINTS, "set", command_ini_bps);
+ add_command_handler(BREAKPOINTS, "add", command_bp_add);
+ add_command_handler(BREAKPOINTS, "change", command_bp_change);
+ add_command_handler(BREAKPOINTS, "enable", command_bp_enable);
+ add_command_handler(BREAKPOINTS, "disable", command_bp_disable);
+ add_command_handler(BREAKPOINTS, "remove", command_bp_remove);
+ add_command_handler(BREAKPOINTS, "getBreakpointIDs", command_get_bp_ids);
+ add_command_handler(BREAKPOINTS, "getProperties", command_get_properties);
+ add_command_handler(BREAKPOINTS, "getStatus", command_get_status);
+}
+
+#endif
diff --git a/breakpoints.h b/breakpoints.h
new file mode 100644
index 00000000..2e486d6e
--- /dev/null
+++ b/breakpoints.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * This module implements Breakpoints service.
+ * The service maintains a list of breakpoints.
+ * Each breakpoint consists of one or more conditions that determine
+ * when a program's execution should be interrupted.
+ */
+
+#ifndef D_breakpoints
+#define D_breakpoints
+
+#include "context.h"
+#include "streams.h"
+
+typedef unsigned long address_t;
+
+typedef struct SkipBreakpointInfo SkipBreakpointInfo;
+
+struct SkipBreakpointInfo {
+ Context * ctx;
+ address_t address;
+ int pending_intercept;
+ void (*done)(SkipBreakpointInfo *);
+ OutputStream * out;
+ char token[256];
+ int error;
+};
+
+extern int is_stopped_by_breakpoint(Context * ctx);
+
+extern int evaluate_breakpoint_condition(Context * ctx);
+
+extern SkipBreakpointInfo * skip_breakpoint(Context * ctx);
+
+extern void ini_breakpoints_service(void);
+
+#endif
diff --git a/channel.c b/channel.c
new file mode 100644
index 00000000..dd952b6f
--- /dev/null
+++ b/channel.c
@@ -0,0 +1,763 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Implements input and output stream over TCP/IP transport and UDP based auto discovery.
+ */
+
+#if _WRS_KERNEL
+# include <vxWorks.h>
+#endif
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include "mdep.h"
+#include "tcf.h"
+#include "channel.h"
+#include "myalloc.h"
+#include "protocol.h"
+#include "events.h"
+#include "exceptions.h"
+#include "trace.h"
+#include "link.h"
+#include "json.h"
+
+#define ESC 3
+#define BUF_SIZE 0x1000
+#define CHANNEL_MAGIC 0x87208956
+
+typedef struct Channel Channel;
+
+struct Channel {
+ int magic;
+ LINK link;
+
+ int socket;
+ pthread_t thread; /* Socket receiving thread */
+ int thread_exited;
+ pthread_mutex_t mutex; /* Channel data access synchronization lock */
+ pthread_cond_t signal;
+ int long_msg; /* Message is longer then buffer, handlig should start before receiving EOM */
+ int waiting_space; /* Receiving thread is waiting for buffer space */
+ int waiting_data; /* Dispatch thread is waiting for data to read (long messages only) */
+ int message_count; /* Number of messages waiting to be dispatched */
+ int event_posted; /* Message handling event is posted to event queue */
+ int lock_cnt; /* Stream lock count, when > 0 channel cannot be deleted */
+ int handling_msg; /* Stream mutex is locked for input message handling */
+
+ /* Input stream buffer */
+ InputStream inp;
+ unsigned char ibuf[BUF_SIZE];
+ int ibuf_inp;
+ int ibuf_out;
+ int eof;
+ int peek;
+
+ /* Output stream state */
+ OutputStream out;
+ char obuf[BUF_SIZE];
+ int obuf_inp;
+ int out_errno;
+};
+
+#define link2channel(A) ((Channel *)((char *)(A) - (int)&((Channel *)0)->link))
+
+static int ip_port = 0;
+static int tcp_server_socket = -1;
+static int udp_server_socket = -1;
+static pthread_t tcp_server_thread = 0;
+static pthread_t udp_server_thread = 0;
+static LINK channels;
+static int suspended = 0;
+
+static void write_all(OutputStream * out, int byte);
+static void flush_all(OutputStream * out);
+OutputStream broadcast_stream = { write_all, flush_all };
+
+static ChannelCloseListener close_listeners[16];
+static int close_listeners_cnt = 0;
+
+static void delete_channel(Channel * c) {
+ int i;
+ trace(LOG_PROTOCOL, "Deleting channel 0x%08x", c);
+ assert(c->lock_cnt == 0);
+ for (i = 0; i < close_listeners_cnt; i++) {
+ close_listeners[i](&c->inp, &c->out);
+ }
+ list_remove(&c->link);
+ c->magic = 0;
+ loc_free(c);
+}
+
+void stream_lock(OutputStream * out) {
+ Channel * channel = (Channel *)((char *)out - offsetof(Channel, out));
+ assert(is_dispatch_thread());
+ assert(channel->magic == CHANNEL_MAGIC);
+ channel->lock_cnt++;
+}
+
+void stream_unlock(OutputStream * out) {
+ Channel * channel = (Channel *)((char *)out - offsetof(Channel, out));
+ assert(is_dispatch_thread());
+ assert(channel->magic == CHANNEL_MAGIC);
+ assert(channel->lock_cnt > 0);
+ channel->lock_cnt--;
+ if (channel->lock_cnt == 0) delete_channel(channel);
+}
+
+int is_stream_closed(OutputStream * out) {
+ Channel * channel = (Channel *)((char *)out - offsetof(Channel, out));
+ assert(is_dispatch_thread());
+ assert(channel->magic == CHANNEL_MAGIC);
+ assert(channel->lock_cnt > 0);
+ return channel->socket < 0;
+}
+
+static void flush_stream(OutputStream * out) {
+ Channel * channel = (Channel *)((char *)out - offsetof(Channel, out));
+ assert(is_dispatch_thread());
+ assert(channel->magic == CHANNEL_MAGIC);
+ if (channel->obuf_inp == 0) return;
+ if (channel->socket < 0) return;
+ if (channel->out_errno) return;
+ if (send(channel->socket, channel->obuf, channel->obuf_inp, 0) < 0) {
+ int err = errno;
+ trace(LOG_PROTOCOL, "Can't sent() on channel 0x%08x: %d %s", channel, err, errno_to_str(err));
+ channel->out_errno = err;
+ }
+ else {
+ channel->obuf_inp = 0;
+ }
+}
+
+static void flush_all(OutputStream * out) {
+ LINK * l = channels.next;
+ assert(is_dispatch_thread());
+ assert(out == &broadcast_stream);
+ while (l != &channels) {
+ flush_stream(&link2channel(l)->out);
+ l = l->next;
+ }
+}
+
+static void write_stream(OutputStream * out, int byte) {
+ Channel * channel = (Channel *)((char *)out - offsetof(Channel, out));
+ int b0 = byte;
+ assert(is_dispatch_thread());
+ assert(channel->magic == CHANNEL_MAGIC);
+ if (channel->socket < 0) return;
+ if (channel->out_errno) return;
+ if (b0 < 0) byte = ESC;
+ channel->obuf[channel->obuf_inp++] = byte;
+ if (channel->obuf_inp == BUF_SIZE) flush_stream(out);
+ if (b0 < 0 || b0 == ESC) {
+ if (b0 == ESC) byte = 0;
+ else if (b0 == MARKER_EOM) byte = 1;
+ else if (b0 == MARKER_EOS) byte = 2;
+ else assert(0);
+ if (channel->socket < 0) return;
+ if (channel->out_errno) return;
+ channel->obuf[channel->obuf_inp++] = byte;
+ if (channel->obuf_inp == BUF_SIZE) flush_stream(out);
+ }
+}
+
+static void write_all(OutputStream * out, int byte) {
+ LINK * l = channels.next;
+ assert(is_dispatch_thread());
+ assert(out == &broadcast_stream);
+ while (l != &channels) {
+ write_stream(&link2channel(l)->out, byte);
+ l = l->next;
+ }
+}
+
+static int read_byte(Channel * channel) {
+ int res;
+ assert(channel->message_count > 0);
+ while (channel->ibuf_inp == channel->ibuf_out) {
+ assert(channel->long_msg);
+ assert(channel->message_count == 1);
+ if (channel->waiting_space) {
+ assert(!channel->waiting_data);
+ pthread_cond_signal(&channel->signal);
+ channel->waiting_space = 0;
+ }
+ if (channel->eof) return MARKER_EOS;
+ if (channel->socket < 0) return MARKER_EOS;
+ assert(!channel->waiting_data);
+ assert(!channel->waiting_space);
+ channel->waiting_data = 1;
+ pthread_cond_wait(&channel->signal, &channel->mutex);
+ assert(!channel->waiting_data);
+ }
+ res = channel->ibuf[channel->ibuf_out];
+ channel->ibuf_out = (channel->ibuf_out + 1) % BUF_SIZE;
+ return res;
+}
+
+static int read_stream(InputStream * inp) {
+ int b;
+ Channel * c = (Channel *)((char *)inp - offsetof(Channel, inp));
+
+ assert(is_dispatch_thread());
+ assert(c->magic == CHANNEL_MAGIC);
+
+ if (c->peek != MARKER_NULL) {
+ assert(c->peek != MARKER_EOM);
+ b = c->peek;
+ c->peek = MARKER_NULL;
+ }
+ else {
+ if (!c->handling_msg) {
+ pthread_mutex_lock(&c->mutex);
+ c->handling_msg = 1;
+ }
+ b = read_byte(c);
+ if (b == ESC) {
+ b = read_byte(c);
+ if (b == 0) {
+ b = ESC;
+ }
+ else if (b == 1) {
+ b = MARKER_EOM;
+ c->message_count--;
+ if (c->waiting_space) {
+ assert(!c->waiting_data);
+ pthread_cond_signal(&c->signal);
+ c->waiting_space = 0;
+ }
+ pthread_mutex_unlock(&c->mutex);
+ c->handling_msg = 0;
+ }
+ else if (b == 2) {
+ b = MARKER_EOS;
+ }
+ }
+ }
+
+ return b;
+}
+
+static int peek_stream(InputStream * inp) {
+ Channel * c = (Channel *)((char *)inp - offsetof(Channel, inp));
+ return c->peek = read_stream(inp);
+}
+
+static void send_eof_and_close(Channel * c, int err) {
+ assert(c->magic == CHANNEL_MAGIC);
+ write_stream(&c->out, MARKER_EOS);
+ write_errno(&c->out, err);
+ c->out.write(&c->out, MARKER_EOM);
+ c->out.flush(&c->out);
+ trace(LOG_PROTOCOL, "Closing socket, channel 0x%08x", c);
+ closesocket(c->socket);
+ c->socket = -1;
+}
+
+static void handle_channel_msg(void * x) {
+ Trap trap;
+ Channel * c = (Channel *)x;
+ assert(is_dispatch_thread());
+ for (;;) {
+ assert(c->magic == CHANNEL_MAGIC);
+ if (!c->handling_msg) {
+ pthread_mutex_lock(&c->mutex);
+ c->handling_msg = 1;
+ }
+ assert(c->event_posted);
+ if (c->thread_exited && (c->socket < 0 || c->message_count == 0 || c->long_msg)) {
+ void * res = NULL;
+ c->event_posted = 0;
+ c->handling_msg = 0;
+ pthread_mutex_unlock(&c->mutex);
+ if (c->thread) pthread_join(c->thread, &res);
+ if (c->socket >= 0) send_eof_and_close(c, 0);
+ stream_unlock(&c->out);
+ return;
+ }
+ if (c->message_count == 0 || suspended) {
+ c->event_posted = 0;
+ c->handling_msg = 0;
+ pthread_mutex_unlock(&c->mutex);
+ break;
+ }
+ if (set_trap(&trap)) {
+ handle_protocol_message(&c->inp, &c->out);
+ clear_trap(&trap);
+ }
+ else {
+ trace(LOG_ALWAYS, "Exception handling protocol message: %d %s",
+ trap.error, errno_to_str(trap.error));
+ c->peek = MARKER_NULL;
+ c->ibuf_out = c->ibuf_inp;
+ c->message_count = 0;
+ send_eof_and_close(c, trap.error);
+ }
+ }
+ broadcast_stream.flush(&broadcast_stream);
+}
+
+static void * stream_socket_handler(void * x) {
+ int i;
+ int esc = 0;
+ Channel * channel = (Channel *)x;
+ unsigned char pkt[BUF_SIZE];
+
+ pthread_mutex_lock(&channel->mutex);
+ while (!channel->eof && channel->socket >= 0) {
+ int err = 0;
+ int rd = 0;
+
+ pthread_mutex_unlock(&channel->mutex);
+ rd = recv(channel->socket, (void *)pkt, sizeof(pkt), 0);
+ err = errno;
+ assert(channel->magic == CHANNEL_MAGIC);
+ pthread_mutex_lock(&channel->mutex);
+
+ if (rd < 0) {
+ trace(LOG_ALWAYS, "Can't read from socket: %s", errno_to_str(errno));
+ if (channel->socket < 0) break;
+ channel->eof = 1;
+ break;
+ }
+
+ if (rd == 0) {
+ trace(LOG_PROTOCOL, "Socket is shutdown by remote peer, channel 0x%08x", channel);
+ channel->eof = 1;
+ break;
+ }
+
+ for (i = 0; i < rd && !channel->eof; i++) {
+ unsigned char ch = pkt[i];
+ int ibuf_next = (channel->ibuf_inp + 1) % BUF_SIZE;
+ while (ibuf_next == channel->ibuf_out) {
+ if (channel->message_count == 0 && !channel->long_msg) {
+ channel->long_msg = 1;
+ channel->message_count = 1;
+ if (!suspended && !channel->event_posted) {
+ post_event(handle_channel_msg, channel);
+ channel->event_posted = 1;
+ }
+ }
+ assert(channel->message_count > 0);
+ assert(!channel->waiting_data);
+ assert(!channel->waiting_space);
+ channel->waiting_space = 1;
+ pthread_cond_wait(&channel->signal, &channel->mutex);
+ assert(ibuf_next == (channel->ibuf_inp + 1) % BUF_SIZE);
+ assert(!channel->waiting_space);
+ }
+ if (esc) {
+ esc = 0;
+ switch (ch) {
+ case 0:
+ /* ESC byte */
+ break;
+ case 1:
+ /* EOM - End Of Message */
+ if (channel->long_msg) {
+ channel->long_msg = 0;
+ assert(channel->message_count == 1);
+ }
+ else {
+ channel->message_count++;
+ if (!suspended && !channel->event_posted) {
+ post_event(handle_channel_msg, channel);
+ channel->event_posted = 1;
+ }
+ }
+ break;
+ case 2:
+ /* EOS - End Of Stream */
+ trace(LOG_PROTOCOL, "End of stream on channel 0x%08x", channel);
+ channel->eof = 1;
+ break;
+ default:
+ /* Invalid escape sequence */
+ trace(LOG_ALWAYS, "Protocol: Invalid escape sequence");
+ channel->eof = 1;
+ ch = 2;
+ break;
+ }
+ }
+ else {
+ esc = ch == ESC;
+ }
+ channel->ibuf[channel->ibuf_inp] = ch;
+ channel->ibuf_inp = ibuf_next;
+ if (channel->waiting_data) {
+ assert(!channel->waiting_space);
+ pthread_cond_signal(&channel->signal);
+ channel->waiting_data = 0;
+ }
+ }
+ }
+ if (channel->waiting_data) {
+ assert(!channel->waiting_space);
+ pthread_cond_signal(&channel->signal);
+ channel->waiting_data = 0;
+ }
+ if (!channel->event_posted) {
+ post_event(handle_channel_msg, channel);
+ channel->event_posted = 1;
+ }
+ channel->thread_exited = 1;
+ pthread_mutex_unlock(&channel->mutex);
+ return NULL;
+}
+
+static void handle_channel_open(void * x) {
+ const int i = 1;
+ int socket = (int)x;
+ Channel * c = NULL;
+ int error;
+
+ if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&i, sizeof(i)) < 0) {
+ trace(LOG_ALWAYS, "Can't set TCP_NODELAY option on a socket: %s", errno_to_str(errno));
+ closesocket(socket);
+ return;
+ }
+
+ c = (Channel *)loc_alloc_zero(sizeof(Channel));
+ c->magic = CHANNEL_MAGIC;
+ pthread_mutex_init(&c->mutex, NULL);
+ pthread_cond_init(&c->signal, NULL);
+ c->socket = socket;
+ c->inp.read = read_stream;
+ c->inp.peek = peek_stream;
+ c->out.write = write_stream;
+ c->out.flush = flush_stream;
+ c->peek = MARKER_NULL;
+
+ list_add_last(&c->link, &channels);
+ stream_lock(&c->out);
+ trace(LOG_PROTOCOL, "Openned channel 0x%08x", c);
+
+ send_hello_message(&c->out);
+ flush_stream(&c->out);
+
+ error = pthread_create(&c->thread, &pthread_create_attr, stream_socket_handler, c);
+ if (error) {
+ trace(LOG_ALWAYS, "Can't create a thread: %d %s", error, errno_to_str(error));
+ send_eof_and_close(c, 0);
+ stream_unlock(&c->out);
+ }
+}
+
+static void * tcp_server_socket_handler(void * x) {
+ for (;;) {
+ struct sockaddr_in sockaddr;
+ int i = sizeof(sockaddr);
+ int socket = accept(tcp_server_socket, (struct sockaddr *)&sockaddr, &i);
+
+ if (socket < 0) {
+ trace(LOG_ALWAYS, "socket accept failed: %d %s", errno, errno_to_str(errno));
+ continue;
+ }
+ post_event(handle_channel_open, (void *)socket);
+ }
+ return 0;
+}
+
+static void event_locator_hello(char * token, InputStream * inp, OutputStream * out) {
+ if (inp->read(inp) != '[') exception(ERR_PROTOCOL);
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ char ch;
+ char service[256];
+ json_read_string(inp, service, sizeof(service));
+ // TODO: remember remote service names
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+}
+
+static void app_char(char * buf, int * pos, char ch) {
+ if (*pos < PKT_SIZE) buf[*pos] = ch;
+ (*pos)++;
+}
+
+static void app_str(char * buf, int * pos, char * str) {
+ while (*str) {
+ if (*pos < PKT_SIZE) buf[*pos] = *str;
+ (*pos)++;
+ str++;
+ }
+}
+
+static void udp_send_info2(struct sockaddr_in * src_addr, struct sockaddr_in * dst_addr) {
+ char str_port[32];
+ char buf[PKT_SIZE];
+ int i = 0;
+
+ buf[i++] = 'T';
+ buf[i++] = 'C';
+ buf[i++] = 'F';
+ buf[i++] = '1';
+ buf[i++] = UDP_ACK_INFO;
+ buf[i++] = 0;
+ buf[i++] = 0;
+ buf[i++] = 0;
+ snprintf(str_port, sizeof(str_port), "%d", ip_port);
+ app_str(buf, &i, "ID=");
+ app_str(buf, &i, "TCP:");
+ app_str(buf, &i, inet_ntoa(src_addr->sin_addr));
+ app_str(buf, &i, ":");
+ app_str(buf, &i, str_port);
+ app_char(buf, &i, 0);
+ app_str(buf, &i, "Name=");
+ app_str(buf, &i, "TCF Agent");
+ app_char(buf, &i, 0);
+ app_str(buf, &i, "OSName=");
+ app_str(buf, &i, get_os_name());
+ app_char(buf, &i, 0);
+ app_str(buf, &i, "TransportName=TCP");
+ app_char(buf, &i, 0);
+ app_str(buf, &i, "Host=");
+ app_str(buf, &i, inet_ntoa(src_addr->sin_addr));
+ app_char(buf, &i, 0);
+ app_str(buf, &i, "Port=");
+ app_str(buf, &i, str_port);
+ app_char(buf, &i, 0);
+ if (sendto(udp_server_socket, buf, i, 0, (struct sockaddr *)dst_addr, sizeof(*dst_addr)) < 0) {
+ trace(LOG_ALWAYS, "Can't send UDP packet to %s: %s",
+ inet_ntoa(dst_addr->sin_addr), errno_to_str(errno));
+ }
+}
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+static void udp_send_info(struct sockaddr_in * addr) {
+ /* If addr == NULL - broadcast */
+#ifdef WIN32
+ int i;
+ MIB_IPADDRTABLE * info = (MIB_IPADDRTABLE *)loc_alloc(sizeof(MIB_IPADDRTABLE));
+ ULONG out_buf_len = sizeof(MIB_IPADDRTABLE);
+ DWORD ret_val = GetIpAddrTable(info, &out_buf_len, 0);
+ if (ret_val == ERROR_INSUFFICIENT_BUFFER) {
+ loc_free(info);
+ info = (MIB_IPADDRTABLE *)loc_alloc(out_buf_len);
+ ret_val = GetIpAddrTable(info, &out_buf_len, 0);
+ }
+ if (ret_val != NO_ERROR) {
+ trace(LOG_ALWAYS, "GetIpAddrTable() error: %d\n", ret_val);
+ loc_free(info);
+ return;
+ }
+ for (i = 0; i < (int)info->dwNumEntries; i++) {
+ unsigned src_net_addr = info->table[i].dwAddr;
+ unsigned src_net_mask = info->table[i].dwMask;
+ struct sockaddr_in src_addr;
+ if (src_net_addr == 0) continue;
+ memset(&src_addr, 0, sizeof src_addr);
+ src_addr.sin_family = AF_INET;
+ src_addr.sin_port = htons((short)ip_port);
+ src_addr.sin_addr.s_addr = src_net_addr;
+ if (addr == NULL) {
+ struct sockaddr_in dst_addr;
+ memset(&dst_addr, 0, sizeof dst_addr);
+ dst_addr.sin_family = PF_INET;
+ dst_addr.sin_port = htons((short)ip_port);
+ dst_addr.sin_addr.s_addr = src_net_addr | ~src_net_mask;
+ udp_send_info2(&src_addr, &dst_addr);
+ }
+ else if ((src_net_addr & src_net_mask) == (addr->sin_addr.s_addr & src_net_mask)) {
+ udp_send_info2(&src_addr, addr);
+ }
+ }
+ loc_free(info);
+#else
+ char if_bbf[0x2000];
+ struct ifconf ifc;
+ char * cp;
+
+ memset(&ifc, 0, sizeof ifc);
+ ifc.ifc_len = sizeof if_bbf;
+ ifc.ifc_buf = if_bbf;
+ if (ioctl(udp_server_socket, SIOCGIFCONF, &ifc) < 0) {
+ trace(LOG_ALWAYS, "error: ioctl(SIOCGIFCONF) returned %d: %s", errno, errno_to_str(errno));
+ return;
+ }
+ cp = (char *)ifc.ifc_req;
+ while (cp < (char *)ifc.ifc_req + ifc.ifc_len) {
+ struct ifreq * ifreq_addr = (struct ifreq *)cp;
+ struct ifreq ifreq_mask = *ifreq_addr;
+ unsigned src_net_addr, src_net_mask;
+ cp += sizeof(ifreq_addr->ifr_name);
+ cp += MAX(SA_LEN(&ifreq_addr->ifr_addr), sizeof(ifreq_addr->ifr_addr));
+ if (ioctl(udp_server_socket, SIOCGIFNETMASK, &ifreq_mask) < 0) {
+ trace(LOG_ALWAYS, "error: ioctl(SIOCGIFNETMASK) returned %d: %s", errno, errno_to_str(errno));
+ continue;
+ }
+ src_net_addr = ((struct sockaddr_in *)&ifreq_addr->ifr_addr)->sin_addr.s_addr;
+ src_net_mask = ((struct sockaddr_in *)&ifreq_mask.ifr_netmask)->sin_addr.s_addr;
+ if (addr == NULL) {
+ struct sockaddr_in dst_addr;
+ memset(&dst_addr, 0, sizeof dst_addr);
+ dst_addr.sin_family = AF_INET;
+ dst_addr.sin_port = htons((short)ip_port);
+ dst_addr.sin_addr.s_addr = src_net_addr | ~src_net_mask;
+ udp_send_info2((struct sockaddr_in *)&ifreq_addr->ifr_addr, &dst_addr);
+ }
+ else if ((src_net_addr & src_net_mask) == (addr->sin_addr.s_addr & src_net_mask)) {
+ udp_send_info2((struct sockaddr_in *)&ifreq_addr->ifr_addr, addr);
+ }
+ }
+#endif
+}
+
+static void * udp_server_socket_handler(void * x) {
+ char buf[PKT_SIZE];
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof addr);
+
+ udp_send_info(NULL);
+ for (;;) {
+ int addr_len = sizeof(addr);
+ int rd = recvfrom(udp_server_socket, buf, PKT_SIZE, 0,
+ (struct sockaddr *)&addr, &addr_len);
+ if (rd < 0) {
+ trace(LOG_ALWAYS, "UDP socket receive failed: %s", errno_to_str(errno));
+ continue;
+ }
+ if (rd == 0) continue;
+ if (buf[0] != 'T' || buf[1] != 'C' || buf[2] != 'F' || buf[3] != '1') {
+ trace(LOG_ALWAYS, "Received malformed UDP packet");
+ continue;
+ }
+ if (buf[4] == UDP_REQ_INFO) {
+ udp_send_info(&addr);
+ }
+ }
+ return NULL;
+}
+
+void channels_suspend(void) {
+ assert(is_dispatch_thread());
+ trace(LOG_PROTOCOL, "All channels suspended");
+ suspended = 1;
+}
+
+int are_channels_suspended(void) {
+ assert(is_dispatch_thread());
+ return suspended;
+}
+
+void channels_resume(void) {
+ LINK * l = channels.next;
+ assert(is_dispatch_thread());
+ assert(suspended);
+ trace(LOG_PROTOCOL, "All channels resumed");
+ suspended = 0;
+ while (l != &channels) {
+ Channel * c = link2channel(l);
+ pthread_mutex_lock(&c->mutex);
+ if (c->message_count > 0 && !c->event_posted) {
+ post_event(handle_channel_msg, c);
+ c->event_posted = 1;
+ }
+ pthread_mutex_unlock(&c->mutex);
+ l = l->next;
+ }
+}
+
+int channels_get_message_count(void) {
+ int cnt = 0;
+ LINK * l = channels.next;
+ assert(is_dispatch_thread());
+ while (l != &channels) {
+ Channel * c = link2channel(l);
+ pthread_mutex_lock(&c->mutex);
+ cnt += c->message_count;
+ pthread_mutex_unlock(&c->mutex);
+ l = l->next;
+ }
+ return cnt;
+}
+
+void add_channel_close_listener(ChannelCloseListener listener) {
+ assert(close_listeners_cnt < sizeof(close_listeners) / sizeof(ChannelCloseListener));
+ close_listeners[close_listeners_cnt++] = listener;
+}
+
+void ini_channel_manager(int port) {
+ const int i = 1;
+ struct sockaddr_in sockaddr;
+
+ add_event_handler("Locator", "Hello", event_locator_hello);
+
+ list_init(&channels);
+ ip_port = port;
+ memset(&sockaddr, 0, sizeof sockaddr);
+ sockaddr.sin_family = PF_INET;
+ sockaddr.sin_port = htons((short)port);
+ sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ tcp_server_socket = socket(PF_INET, SOCK_STREAM, 0);
+ if (tcp_server_socket < 0) {
+ perror("Can't create a socket");
+ exit(1);
+ }
+ /* Allow rapid reuse of this port. */
+ if (setsockopt(tcp_server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)) < 0) {
+ perror("Can't set options on a socket");
+ exit(1);
+ }
+ if (bind(tcp_server_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
+ perror("Can't bind server TCP socket address");
+ exit(1);
+ }
+ if (listen(tcp_server_socket, 16)) {
+ perror("Can't listen on server socket");
+ exit(1);
+ }
+ if (pthread_create(&tcp_server_thread, &pthread_create_attr, tcp_server_socket_handler, 0) != 0) {
+ perror("Can't create socket listener thread");
+ exit(1);
+ }
+
+ udp_server_socket = socket(PF_INET, SOCK_DGRAM, 0);
+ if (udp_server_socket < 0) {
+ perror("Can't create a socket");
+ exit(1);
+ }
+ if (setsockopt(udp_server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)) < 0) {
+ perror("Can't set SO_REUSEADDR option on a socket");
+ exit(1);
+ }
+ if (setsockopt(udp_server_socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) {
+ perror("Can't set SO_BROADCAST option on a socket");
+ exit(1);
+ }
+ if (bind(udp_server_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
+ perror("Can't bind server UDP socket address");
+ exit(1);
+ }
+ if (pthread_create(&udp_server_thread, &pthread_create_attr, udp_server_socket_handler, 0) != 0) {
+ perror("Can't create a thread");
+ exit(1);
+ }
+
+#ifndef WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+}
diff --git a/channel.h b/channel.h
new file mode 100644
index 00000000..20cf01a2
--- /dev/null
+++ b/channel.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Implements input and output stream over TCP/IP transport and UDP based auto discovery.
+ */
+
+#ifndef D_channel
+#define D_channel
+
+#include "streams.h"
+
+extern OutputStream broadcast_stream;
+
+/*
+ * Temporary suspend handling of incoming messages on all channels
+ */
+extern void channels_suspend(void);
+
+/*
+ * Returns 1 if handling of incoming messages is suspended.
+ */
+extern int are_channels_suspended(void);
+
+/*
+ * Resume handling of messages on all channels.
+ */
+extern void channels_resume(void);
+
+/*
+ * Return number of pending input messages on all channels.
+ */
+extern int channels_get_message_count(void);
+
+/*
+ * Lock OutputStream to prevent it from being deleted.
+ * A stream must be locked to keep a referense to it across event dispatch cycles.
+ */
+extern void stream_lock(OutputStream * out);
+extern void stream_unlock(OutputStream * out);
+
+/*
+ * Check if stream is closed. Onlu make sense when the stream is locked.
+ * Unlocked stream is deleted when closed.
+ */
+extern int is_stream_closed(OutputStream * out);
+
+/*
+ * Register channel close callback.
+ * Service implementation can use the callback to deallocate resources
+ * after a client disconnects.
+ */
+typedef void (*ChannelCloseListener)(InputStream *, OutputStream *);
+extern void add_channel_close_listener(ChannelCloseListener listener);
+
+/*
+ * Initialize channel manager.
+ * 'port' - listenig socket port.
+ */
+extern void ini_channel_manager(int port);
+
+#endif
diff --git a/cmdline.c b/cmdline.c
new file mode 100644
index 00000000..1d47d27a
--- /dev/null
+++ b/cmdline.c
@@ -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
+ *******************************************************************************/
+
+/*
+ * Command line interpreter.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "mdep.h"
+#include "context.h"
+#include "events.h"
+#include "myalloc.h"
+
+static pthread_t interactive_thread;
+
+static void cmd_list_contexts(char *s) {
+ LINK * qp;
+ for (qp = context_root.next; qp != &context_root; qp = qp->next) {
+ Context * ctx = ctxl2ctxp(qp);
+ printf("ctx %#x pid %d state %s\n", ctx, ctx->pid, context_state_name(ctx));
+ }
+}
+
+static void cmd_exit(char *s) {
+ exit(0);
+}
+
+static void event_cmd_line(void * arg) {
+ char * s = (char *)arg;
+ int len;
+ struct {
+ char *cmd;
+ void (*hnd)(char *);
+ } cmds[] = {
+ { "list-contexts", cmd_list_contexts },
+ { "exit", cmd_exit },
+ { 0 }
+ }, *cp;
+
+ while (*s && isspace(*s)) s++;
+ for (cp = cmds; cp->cmd != 0; cp++) {
+ len = strlen(cp->cmd);
+ if (strncmp(s, cp->cmd, len) == 0 && (s[len] == 0 || isspace(s[len]))) {
+ s += len;
+ while (*s && isspace(*s)) s++;
+ cp->hnd(s);
+ break;
+ }
+ }
+ if (cp->cmd == 0) {
+ fprintf(stderr, "unknown command: %s\n", s);
+ }
+ loc_free(arg);
+}
+
+static void * interactive_handler(void *x) {
+ char buf[1000];
+
+ while (fgets(buf, sizeof(buf), stdin) != NULL) {
+ char * s = (char *)loc_alloc(strlen(buf) + 1);
+ strcpy(s, buf);
+ post_event(event_cmd_line, s);
+ }
+ return NULL;
+}
+
+void ini_cmdline_handler(void) {
+ /* Create thread to read cmd line */
+ if (pthread_create(&interactive_thread, &pthread_create_attr, interactive_handler, 0) != 0) {
+ perror("pthread_create");
+ exit(1);
+ }
+}
+
diff --git a/cmdline.h b/cmdline.h
new file mode 100644
index 00000000..63392dd9
--- /dev/null
+++ b/cmdline.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Command line interpreter.
+ */
+
+#ifndef D_cmdline
+#define D_cmdline
+
+extern void ini_cmdline_handler(void);
+
+#endif
diff --git a/config.h b/config.h
new file mode 100644
index 00000000..e1660fba
--- /dev/null
+++ b/config.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * This file contains "define" statements that control agent configuration.
+ * SERVICE_* definitions control which service implementations are included into the agent.
+ */
+
+#if defined(WIN32)
+# define TARGET_WINDOWS 1
+# define TARGET_VXWORKS 0
+# define TARGET_LINUX 0
+#elif defined(_WRS_KERNEL)
+# define TARGET_WINDOWS 0
+# define TARGET_VXWORKS 1
+# define TARGET_LINUX 0
+#else
+# define TARGET_WINDOWS 0
+# define TARGET_VXWORKS 0
+# define TARGET_LINUX 1
+#endif
+
+#define SERVICE_RunControl TARGET_LINUX || TARGET_VXWORKS
+#define SERVICE_Breakpoints TARGET_LINUX || TARGET_VXWORKS
+#define SERVICE_Memory TARGET_LINUX || TARGET_VXWORKS
+#define SERVICE_Registers TARGET_LINUX || TARGET_VXWORKS
+#define SERVICE_StackTrace TARGET_LINUX || TARGET_VXWORKS
+#define SERVICE_Symbols TARGET_LINUX || TARGET_VXWORKS
+#define SERVICE_LineNumbers TARGET_LINUX
+#define SERVICE_Processes TARGET_LINUX || TARGET_VXWORKS
+#define SERVICE_FileSystem TARGET_LINUX || TARGET_VXWORKS || TARGET_WINDOWS
+#define SERVICE_SysMonitor TARGET_LINUX
+
+
+
diff --git a/context.c b/context.c
new file mode 100644
index 00000000..affdfde2
--- /dev/null
+++ b/context.c
@@ -0,0 +1,1159 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module handles process/thread OS contexts and their state machine.
+ */
+
+#if defined(_WRS_KERNEL)
+# include <vxWorks.h>
+#endif
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include "context.h"
+#include "events.h"
+#include "errors.h"
+#include "trace.h"
+#include "myalloc.h"
+
+#define CONTEXT_PID_ROOT_SIZE 1024
+#define CONTEXT_PID_HASH(PID) ((PID) % CONTEXT_PID_ROOT_SIZE)
+static LINK context_pid_root[CONTEXT_PID_ROOT_SIZE];
+static ContextEventListener * event_listeners = NULL;
+
+LINK context_root = { NULL, NULL };
+
+#define CASE(var) case var: return ""#var;
+char * signal_name(int signal) {
+#ifndef WIN32
+ switch (signal) {
+ CASE(SIGHUP)
+ CASE(SIGINT)
+ CASE(SIGQUIT)
+ CASE(SIGILL)
+ CASE(SIGTRAP)
+ CASE(SIGABRT)
+ CASE(SIGBUS)
+ CASE(SIGFPE)
+ CASE(SIGKILL)
+ CASE(SIGUSR1)
+ CASE(SIGSEGV)
+ CASE(SIGUSR2)
+ CASE(SIGPIPE)
+ CASE(SIGALRM)
+ CASE(SIGTERM)
+#ifndef _WRS_KERNEL
+ CASE(SIGSTKFLT)
+#endif
+ CASE(SIGCHLD)
+ CASE(SIGCONT)
+ CASE(SIGSTOP)
+ CASE(SIGTSTP)
+ CASE(SIGTTIN)
+ CASE(SIGTTOU)
+ CASE(SIGURG)
+ CASE(SIGXCPU)
+ CASE(SIGXFSZ)
+ CASE(SIGVTALRM)
+ CASE(SIGPROF)
+#ifndef _WRS_KERNEL
+ CASE(SIGWINCH)
+ CASE(SIGIO)
+ CASE(SIGPWR)
+#endif
+ CASE(SIGSYS)
+ }
+#endif
+ return NULL;
+}
+#undef CASE
+
+Context * context_find_from_pid(pid_t pid) {
+ LINK * qhp = &context_pid_root[CONTEXT_PID_HASH(pid)];
+ LINK * qp;
+
+ assert(is_dispatch_thread());
+ for (qp = qhp->next; qp != qhp; qp = qp->next) {
+ Context * ctx = pidl2ctxp(qp);
+ if (ctx->pid == pid && !ctx->exited) return ctx;
+ }
+ return NULL;
+}
+
+static Context * create_context(pid_t pid) {
+ LINK * qhp = &context_pid_root[CONTEXT_PID_HASH(pid)];
+ Context * ctx = (Context *)loc_alloc_zero(sizeof(Context));
+
+ assert(context_find_from_pid(pid) == NULL);
+ ctx->pid = pid;
+ ctx->ref_count = 1;
+ list_init(&ctx->children);
+ list_add_first(&ctx->ctxl, &context_root);
+ list_add_first(&ctx->pidl, qhp);
+ return ctx;
+}
+
+char * pid2id(pid_t pid, pid_t parent) {
+ static char s[64];
+ char * p = s + sizeof(s);
+ unsigned long n = (long)pid;
+ *(--p) = 0;
+ do {
+ *(--p) = (char)(n % 10 + '0');
+ n = n / 10;
+ }
+ while (n != 0);
+ if (parent != 0) {
+ n = (long)parent;
+ *(--p) = '.';
+ do {
+ *(--p) = (char)(n % 10 + '0');
+ n = n / 10;
+ }
+ while (n != 0);
+ }
+ *(--p) = 'P';
+ return p;
+}
+
+char * thread_id(Context * ctx) {
+ if (ctx->parent == NULL) return pid2id(ctx->pid, ctx->pid);
+ assert(ctx->parent->parent == NULL);
+ return pid2id(ctx->pid, ctx->parent->pid);
+}
+
+char * container_id(Context * ctx) {
+ if (ctx->parent != NULL) ctx = ctx->parent;
+ assert(ctx->parent == NULL);
+ return pid2id(ctx->pid, 0);
+}
+
+pid_t id2pid(char * id, pid_t * parent) {
+ pid_t pid = 0;
+ if (parent != NULL) *parent = 0;
+ if (id == NULL) return 0;
+ if (id[0] != 'P') return 0;
+ if (id[1] == 0) return 0;
+ pid = (pid_t)strtol(id + 1, &id, 10);
+ if (id[0] == '.') {
+ if (id[1] == 0) return 0;
+ if (parent != NULL) *parent = pid;
+ pid = (pid_t)strtol(id + 1, &id, 10);
+ }
+ if (id[0] != 0) return 0;
+ return pid;
+}
+
+Context * id2ctx(char * id) {
+ pid_t pid = id2pid(id, NULL);
+ if (pid == 0) return NULL;
+ return context_find_from_pid(pid);
+}
+
+void context_lock(Context * ctx) {
+ assert(ctx->ref_count > 0);
+ ctx->ref_count++;
+}
+
+void context_unlock(Context * ctx) {
+ if (--(ctx->ref_count) == 0) {
+ assert(list_is_empty(&ctx->children));
+ assert(ctx->parent == NULL);
+ list_remove(&ctx->ctxl);
+ list_remove(&ctx->pidl);
+ loc_free(ctx);
+ }
+}
+
+char * context_state_name(Context * ctx) {
+ if (ctx->exited) return "exited";
+ if (ctx->intercepted) return "intercepted";
+ if (ctx->stopped) return "stopped";
+ return "running";
+}
+
+static void event_context_created(Context * ctx) {
+ ContextEventListener * listener = event_listeners;
+ while (listener != NULL) {
+ if (listener->context_created != NULL) listener->context_created(ctx);
+ listener = listener->next;
+ }
+}
+
+static void event_context_changed(Context * ctx) {
+ ContextEventListener * listener = event_listeners;
+ while (listener != NULL) {
+ if (listener->context_changed != NULL) listener->context_changed(ctx);
+ listener = listener->next;
+ }
+}
+
+static void event_context_stopped(Context * ctx) {
+ ContextEventListener * listener = event_listeners;
+ while (listener != NULL) {
+ if (listener->context_stopped != NULL) listener->context_stopped(ctx);
+ listener = listener->next;
+ }
+}
+
+static void event_context_started(Context * ctx) {
+ ContextEventListener * listener = event_listeners;
+ while (listener != NULL) {
+ if (listener->context_started != NULL) listener->context_started(ctx);
+ listener = listener->next;
+ }
+}
+
+static void event_context_exited(Context * ctx) {
+ ContextEventListener * listener = event_listeners;
+ while (listener != NULL) {
+ if (listener->context_exited != NULL) listener->context_exited(ctx);
+ listener = listener->next;
+ }
+}
+
+#ifdef WIN32
+
+/*
+ * On Windows context management is not supported yet.
+ */
+
+char * event_name(int event) {
+ return "Unknown";
+}
+
+int context_attach(pid_t pid, Context ** res) {
+ errno = EINVAL;
+ return -1;
+}
+
+int context_stop(Context * ctx) {
+ errno = EINVAL;
+ return -1;
+}
+
+int context_continue(Context * ctx) {
+ errno = EINVAL;
+ return -1;
+}
+
+int context_single_step(Context * ctx) {
+ errno = EINVAL;
+ return -1;
+}
+
+int context_read_mem(Context * ctx, unsigned long address, void * buf, size_t size) {
+ errno = EINVAL;
+ return -1;
+}
+
+int context_write_mem(Context * ctx, unsigned long address, void * buf, size_t size) {
+ errno = EINVAL;
+ return -1;
+}
+
+static void init(void) {
+}
+
+#elif defined(_WRS_KERNEL)
+
+/* TODO: RTP support */
+
+#include <taskHookLib.h>
+#include <private/vxdbgLibP.h>
+
+#define TRACE_EVENT_STEP 2
+
+#define EVENT_HOOK_IGNORE 1
+#define EVENT_HOOK_BREAKPOINT 2
+#define EVENT_HOOK_STEP_DONE 3
+#define EVENT_HOOK_STOP 4
+#define EVENT_HOOK_TASK_ADD 5
+#define EVENT_HOOK_TASK_DEL 6
+
+struct event_info {
+ int event;
+ VXDBG_CTX current_ctx; /* context that hit breakpoint */
+ VXDBG_CTX stopped_ctx; /* context stopped by the breakpoint */
+ REG_SET regs; /* task registers before exception */
+ UINT32 addr; /* breakpoint addr */
+ int bp_info_ok; /* breakpoint information available */
+ VXDBG_BP_INFO bp_info; /* breakpoint information */
+ SEM_ID delete_signal;
+};
+
+VXDBG_CLNT_ID vxdbg_clnt_id = 0;
+
+#define MAX_EVENTS 64
+static struct event_info events[MAX_EVENTS];
+static int events_inp = 0;
+static int events_out = 0;
+static int events_buf_overflow = 0;
+static spinlockIsr_t events_lock;
+static VX_COUNTING_SEMAPHORE(events_signal_mem);
+static SEM_ID events_signal;
+static pthread_t events_thread;
+static WIND_TCB * main_thread;
+
+char * event_name(int event) {
+ switch (event) {
+ case 0: return "none";
+ case TRACE_EVENT_STEP: return "Single Step";
+ }
+ return NULL;
+}
+
+static struct event_info * event_info_alloc(int event) {
+ int nxt;
+ struct event_info * info;
+ SPIN_LOCK_ISR_TAKE(&events_lock);
+ if (events_buf_overflow) {
+ SPIN_LOCK_ISR_GIVE(&events_lock);
+ return NULL;
+ }
+ info = events + events_inp;
+ nxt = (events_inp + 1) % MAX_EVENTS;
+ if (nxt == events_out) {
+ events_buf_overflow = 1;
+ semGive(events_signal);
+ SPIN_LOCK_ISR_GIVE(&events_lock);
+ return NULL;
+ }
+ memset(info, 0, sizeof(struct event_info));
+ info->event = event;
+ events_inp = nxt;
+ return info;
+}
+
+static void event_info_post(struct event_info * info) {
+ assert(info != NULL);
+ semGive(events_signal);
+ SPIN_LOCK_ISR_GIVE(&events_lock);
+}
+
+int context_attach(pid_t pid, Context ** res) {
+ struct event_info * info;
+ Context * ctx = create_context(pid);
+
+ ctx->mem = taskIdSelf();
+ assert(ctx->ref_count == 1);
+ event_context_created(ctx);
+ if (taskIsStopped(pid)) {
+ ctx->pending_intercept = 1;
+ info = event_info_alloc(EVENT_HOOK_STOP);
+ if (info != NULL) {
+ info->stopped_ctx.ctxId = pid;
+ event_info_post(info);
+ }
+ }
+ if (res != NULL) *res = ctx;
+ return 0;
+}
+
+int context_stop(Context * ctx) {
+ struct event_info * info;
+ VXDBG_CTX vxdbg_ctx;
+
+ assert(is_dispatch_thread());
+ assert(!ctx->stopped);
+ assert(!ctx->exited);
+ assert(!ctx->regs_dirty);
+ assert(!ctx->intercepted);
+ if (ctx->pending_intercept) {
+ trace(LOG_CONTEXT, "context: stop ctx %#x pid %d", ctx, ctx->pid);
+ }
+ else {
+ trace(LOG_CONTEXT, "context: temporary stop ctx %#x pid %d", ctx, ctx->pid);
+ }
+
+ vxdbg_ctx.ctxId = ctx->pid;
+ vxdbg_ctx.ctxType = VXDBG_CTX_TASK;
+ if (vxdbgStop(vxdbg_clnt_id, &vxdbg_ctx) != OK) return -1;
+ assert(taskIsStopped(ctx->pid));
+
+ info = event_info_alloc(EVENT_HOOK_STOP);
+ if (info != NULL) {
+ info->stopped_ctx.ctxId = ctx->pid;
+ event_info_post(info);
+ }
+ return 0;
+}
+
+static int kill_context(Context * ctx) {
+ ctx->pending_signals &= ~(1 << SIGKILL);
+ if (taskDelete(ctx->pid) != OK) return -1;
+ ctx->stopped = 0;
+ event_context_started(ctx);
+ ctx->exiting = 0;
+ ctx->exited = 1;
+ event_context_exited(ctx);
+ if (ctx->parent != NULL) {
+ list_remove(&ctx->cldl);
+ context_unlock(ctx->parent);
+ ctx->parent = NULL;
+ }
+ context_unlock(ctx);
+ return 0;
+}
+
+int context_continue(Context * ctx) {
+ VXDBG_CTX vxdbg_ctx;
+
+ assert(is_dispatch_thread());
+ assert(ctx->stopped);
+ assert(!ctx->pending_intercept);
+ assert(!ctx->exited);
+ assert(!ctx->pending_step);
+ trace(LOG_CONTEXT, "context: resume ctx %#x, pid %d", ctx, ctx->pid);
+
+ if (ctx->regs_dirty) {
+ if (taskRegsSet(ctx->pid, &ctx->regs) != OK) return -1;
+ ctx->regs_dirty = 0;
+ }
+
+ if (ctx->pending_signals & (1 << SIGKILL)) {
+ return kill_context(ctx);
+ }
+
+ vxdbg_ctx.ctxId = ctx->pid;
+ vxdbg_ctx.ctxType = VXDBG_CTX_TASK;
+ if (vxdbgCont(vxdbg_clnt_id, &vxdbg_ctx) != OK) return -1;
+ ctx->stopped = 0;
+ event_context_started(ctx);
+ return 0;
+}
+
+int context_single_step(Context * ctx) {
+ VXDBG_CTX vxdbg_ctx;
+ struct event_info * info;
+
+ assert(is_dispatch_thread());
+ assert(ctx->stopped);
+ assert(!ctx->pending_intercept);
+ assert(!ctx->pending_step);
+ assert(!ctx->exited);
+ trace(LOG_CONTEXT, "context: single step ctx %#x, pid %d", ctx, ctx->pid);
+
+ if (ctx->regs_dirty) {
+ if (taskRegsSet(ctx->pid, &ctx->regs) != OK) return -1;
+ ctx->regs_dirty = 0;
+ }
+
+ if (ctx->pending_signals & (1 << SIGKILL)) {
+ return kill_context(ctx);
+ }
+
+ vxdbg_ctx.ctxId = ctx->pid;
+ vxdbg_ctx.ctxType = VXDBG_CTX_TASK;
+ if (vxdbgStep(vxdbg_clnt_id, &vxdbg_ctx, NULL, NULL) != OK) return -1;
+ ctx->pending_step = 1;
+ ctx->stopped = 0;
+ event_context_started(ctx);
+ return 0;
+}
+
+int context_read_mem(Context * ctx, unsigned long address, void * buf, size_t size) {
+#ifdef _WRS_PERSISTENT_SW_BP
+ vxdbgMemRead((void *)address, buf, size);
+#else
+ bcopy((void *)address, buf, size);
+#endif
+ return 0;
+}
+
+int context_write_mem(Context * ctx, unsigned long address, void * buf, size_t size) {
+#ifdef _WRS_PERSISTENT_SW_BP
+ vxdbgMemWrite((void *)address, buf, size);
+#else
+ bcopy(buf, (void *)address, size);
+#endif
+ return 0;
+}
+
+static void event_handler(void * arg) {
+ struct event_info * info = (struct event_info *)arg;
+ Context * current_ctx = context_find_from_pid(info->current_ctx.ctxId);
+ Context * stopped_ctx = context_find_from_pid(info->stopped_ctx.ctxId);
+
+ switch (info->event) {
+ case EVENT_HOOK_BREAKPOINT:
+ if (stopped_ctx == NULL) break;
+ assert(!stopped_ctx->stopped);
+ assert(!stopped_ctx->regs_dirty);
+ assert(!stopped_ctx->intercepted);
+ stopped_ctx->regs_error = 0;
+ stopped_ctx->regs = info->regs;
+ stopped_ctx->signal = SIGTRAP;
+ assert(get_regs_PC(stopped_ctx->regs) == info->addr);
+ stopped_ctx->event = 0;
+ stopped_ctx->stopped_by_bp = info->bp_info_ok;
+ stopped_ctx->bp_info = info->bp_info;
+ if (current_ctx != NULL) stopped_ctx->bp_pid = current_ctx->pid;
+ stopped_ctx->pending_step = 0;
+ stopped_ctx->stopped = 1;
+ event_context_stopped(stopped_ctx);
+ break;
+ case EVENT_HOOK_STEP_DONE:
+ if (current_ctx == NULL) break;
+ assert(!current_ctx->stopped);
+ assert(!current_ctx->regs_dirty);
+ assert(!current_ctx->intercepted);
+ current_ctx->regs_error = 0;
+ current_ctx->regs = info->regs;
+ current_ctx->signal = SIGTRAP;
+ current_ctx->event = TRACE_EVENT_STEP;
+ current_ctx->pending_step = 0;
+ current_ctx->stopped = 1;
+ event_context_stopped(current_ctx);
+ break;
+ case EVENT_HOOK_STOP:
+ if (stopped_ctx == NULL) break;
+ assert(!stopped_ctx->stopped);
+ stopped_ctx->regs_error = 0;
+ if (taskRegsGet(stopped_ctx->pid, &stopped_ctx->regs) != OK) {
+ stopped_ctx->regs_error = errno;
+ assert(stopped_ctx->regs_error != 0);
+ }
+ stopped_ctx->signal = SIGSTOP;
+ stopped_ctx->event = 0;
+ stopped_ctx->pending_step = 0;
+ stopped_ctx->stopped = 1;
+ event_context_stopped(stopped_ctx);
+ break;
+ case EVENT_HOOK_TASK_ADD:
+ if (current_ctx == NULL) break;
+ assert(stopped_ctx == NULL);
+ stopped_ctx = create_context((pid_t)info->stopped_ctx.ctxId);
+ assert(stopped_ctx->ref_count == 1);
+ stopped_ctx->mem = current_ctx->mem;
+ stopped_ctx->parent = current_ctx->parent != NULL ? current_ctx->parent : current_ctx;
+ stopped_ctx->parent->ref_count++;
+ list_add_first(&stopped_ctx->cldl, &stopped_ctx->parent->children);
+ event_context_created(stopped_ctx);
+ break;
+ case EVENT_HOOK_TASK_DEL:
+ if (stopped_ctx != NULL) {
+ assert(!stopped_ctx->stopped);
+ assert(!stopped_ctx->intercepted);
+ assert(!stopped_ctx->exited);
+ stopped_ctx->pending_step = 0;
+ stopped_ctx->exiting = 0;
+ stopped_ctx->exited = 1;
+ event_context_exited(stopped_ctx);
+ if (stopped_ctx->parent != NULL) {
+ list_remove(&stopped_ctx->cldl);
+ context_unlock(stopped_ctx->parent);
+ stopped_ctx->parent = NULL;
+ }
+ context_unlock(stopped_ctx);
+ }
+ semGive(info->delete_signal);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ loc_free(info);
+}
+
+static void event_error(void * arg) {
+ trace(LOG_ALWAYS, "Fatal error: VXDBG events buffer overflow");
+ exit(1);
+}
+
+static void * event_thread_func(void * arg) {
+ struct event_info * info;
+
+ taskPrioritySet(0, VX_TASK_PRIORITY_MIN);
+ for (;;) {
+ semTake(events_signal, WAIT_FOREVER);
+ info = (struct event_info *)loc_alloc(sizeof(struct event_info));
+
+ SPIN_LOCK_ISR_TAKE(&events_lock);
+ if (events_buf_overflow && events_inp == events_out) {
+ SPIN_LOCK_ISR_GIVE(&events_lock);
+ break;
+ }
+ assert(events_inp != events_out);
+ *info = events[events_out];
+ events_out = (events_out + 1) % MAX_EVENTS;
+ SPIN_LOCK_ISR_GIVE(&events_lock);
+
+ if (info->event != EVENT_HOOK_IGNORE) {
+ post_event(event_handler, info);
+ }
+ }
+ post_event(event_error, NULL);
+}
+
+static void vxdbg_event_hook(
+ VXDBG_CTX * current_ctx, /* context that hit breakpoint */
+ VXDBG_CTX * stopped_ctx, /* context stopped by the breakpoint */
+ REG_SET * regs, /* task registers before exception */
+ UINT32 addr, /* breakpoint addr */
+ VXDBG_BP_INFO * bp_info) { /* breakpoint information */
+
+ struct event_info * info = event_info_alloc(EVENT_HOOK_BREAKPOINT);
+ if (info != NULL) {
+ if (stopped_ctx == NULL) info->event = EVENT_HOOK_STEP_DONE;
+ if (current_ctx != NULL) info->current_ctx = *current_ctx;
+ if (stopped_ctx != NULL) info->stopped_ctx = *stopped_ctx;
+ if (regs != NULL) info->regs = *regs;
+ info->addr = addr;
+ if (bp_info != NULL) {
+ info->bp_info_ok = 1;
+ info->bp_info = *bp_info;
+ }
+ event_info_post(info);
+ }
+}
+
+static void task_create_hook(WIND_TCB * tcb) {
+ struct event_info * info = event_info_alloc(EVENT_HOOK_TASK_ADD);
+ if (info != NULL) {
+ info->current_ctx.ctxId = taskIdSelf();
+ info->stopped_ctx.ctxId = (UINT32)tcb;
+ event_info_post(info);
+ }
+}
+
+static void task_delete_hook(WIND_TCB * tcb) {
+ if (tcb != main_thread && taskIdCurrent != main_thread) {
+ struct event_info * info = event_info_alloc(EVENT_HOOK_TASK_DEL);
+ if (info != NULL) {
+ VX_COUNTING_SEMAPHORE(signal_mem);
+ SEM_ID signal = info->delete_signal = semCInitialize(signal_mem, SEM_Q_FIFO, 0);
+ info->current_ctx.ctxId = taskIdSelf();
+ info->stopped_ctx.ctxId = (UINT32)tcb;
+ event_info_post(info);
+ semTake(signal, WAIT_FOREVER);
+ semTerminate(signal);
+ }
+ }
+}
+
+static void init(void) {
+ SPIN_LOCK_ISR_INIT(&events_lock, 0);
+ main_thread = taskIdCurrent;
+ if ((events_signal = semCInitialize(events_signal_mem, SEM_Q_FIFO, 0)) == NULL) {
+ perror("semCInitialize");
+ exit(1);
+ }
+ vxdbg_clnt_id = vxdbgClntRegister(EVT_BP);
+ if (vxdbg_clnt_id == NULL) {
+ perror("vxdbgClntRegister");
+ exit(1);
+ }
+ taskCreateHookAdd((FUNCPTR)task_create_hook);
+ taskDeleteHookAdd((FUNCPTR)task_delete_hook);
+ vxdbgHookAdd(vxdbg_clnt_id, EVT_BP, vxdbg_event_hook);
+ vxdbgHookAdd(vxdbg_clnt_id, EVT_TRACE, vxdbg_event_hook);
+ if (pthread_create(&events_thread, &pthread_create_attr, event_thread_func, NULL) != 0) {
+ perror("pthread_create");
+ exit(1);
+ }
+}
+
+#else
+
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sched.h>
+
+#define PTRACE_SETOPTIONS 0x4200
+#define PTRACE_GETEVENTMSG 0x4201
+#define PTRACE_GETSIGINFO 0x4202
+#define PTRACE_SETSIGINFO 0x4203
+
+#define PTRACE_O_TRACESYSGOOD 0x00000001
+#define PTRACE_O_TRACEFORK 0x00000002
+#define PTRACE_O_TRACEVFORK 0x00000004
+#define PTRACE_O_TRACECLONE 0x00000008
+#define PTRACE_O_TRACEEXEC 0x00000010
+#define PTRACE_O_TRACEVFORKDONE 0x00000020
+#define PTRACE_O_TRACEEXIT 0x00000040
+
+#define PTRACE_EVENT_FORK 1
+#define PTRACE_EVENT_VFORK 2
+#define PTRACE_EVENT_CLONE 3
+#define PTRACE_EVENT_EXEC 4
+#define PTRACE_EVENT_VFORK_DONE 5
+#define PTRACE_EVENT_EXIT 6
+
+#define USE_ESRCH_WORKAROUND 1
+
+#define WORD_SIZE 4
+
+#define PTRACE_FLAGS ( \
+ PTRACE_O_TRACEFORK | \
+ PTRACE_O_TRACEVFORK | \
+ PTRACE_O_TRACECLONE | \
+ PTRACE_O_TRACEEXEC | \
+ PTRACE_O_TRACEVFORKDONE | \
+ PTRACE_O_TRACEEXIT)
+
+struct pid_exit_info {
+ pid_t pid;
+ int value;
+};
+
+struct pid_stop_info {
+ pid_t pid;
+ int signal;
+ int event;
+};
+
+static pthread_t wpid_thread;
+static pid_t my_pid = 0;
+static pthread_mutex_t waitpid_lock;
+static pthread_cond_t waitpid_cond;
+static int attach_flag = 0;
+
+char * event_name(int event) {
+ switch (event) {
+ case 0: return "none";
+ case PTRACE_EVENT_FORK: return "fork";
+ case PTRACE_EVENT_VFORK: return "vfork";
+ case PTRACE_EVENT_CLONE: return "clone";
+ case PTRACE_EVENT_EXEC: return "exec";
+ case PTRACE_EVENT_VFORK_DONE: return "vfork-done";
+ case PTRACE_EVENT_EXIT: return "exit";
+ default:
+ trace(LOG_ALWAYS, "event_name() called with unexpected event code %d", event);
+ return "unknown";
+ }
+}
+
+int context_attach(pid_t pid, Context ** res) {
+ Context * ctx = NULL;
+ pthread_mutex_lock(&waitpid_lock);
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) {
+ int err = errno;
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_ATTACH) failed: pid %d, error %d %s",
+ pid, err, errno_to_str(err));
+ pthread_mutex_unlock(&waitpid_lock);
+ errno = err;
+ return -1;
+ }
+ ctx = create_context(pid);
+ /* TODO: context_attach works only for main task in a process */
+ ctx->mem = pid;
+ assert(ctx->ref_count == 1);
+ event_context_created(ctx);
+ attach_flag = 1;
+ pthread_cond_signal(&waitpid_cond);
+ pthread_mutex_unlock(&waitpid_lock);
+ if (res != NULL) *res = ctx;
+ return 0;
+}
+
+int context_stop(Context * ctx) {
+ assert(is_dispatch_thread());
+ assert(!ctx->exited);
+ assert(!ctx->stopped);
+ assert(!ctx->regs_dirty);
+ assert(!ctx->intercepted);
+ if (ctx->pending_intercept) {
+ trace(LOG_CONTEXT, "context: suspending ctx %#x pid %d", ctx, ctx->pid);
+ }
+ else {
+ trace(LOG_CONTEXT, "context: temporary suspending ctx %#x pid %d", ctx, ctx->pid);
+ }
+ if (tkill(ctx->pid, SIGSTOP) < 0) {
+ int err = errno;
+ trace(LOG_ALWAYS, "error: tkill(SIGSTOP) failed: ctx %#x, pid %d, error %d %s",
+ ctx, ctx->pid, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ return 0;
+}
+
+int context_continue(Context * ctx) {
+ int signal = 0;
+ if (ctx->pending_signals != 0) {
+ while ((ctx->pending_signals & (1 << signal)) == 0) signal++;
+ }
+ assert(signal != SIGSTOP);
+ assert(signal != SIGTRAP);
+ assert(is_dispatch_thread());
+ assert(ctx->stopped);
+ assert(!ctx->pending_intercept);
+ assert(!ctx->pending_step);
+ assert(!ctx->exited);
+ trace(LOG_CONTEXT, "context: resuming ctx %#x, pid %d, with signal %d", ctx, ctx->pid, signal);
+#ifdef __i386__
+ /* Bug in ptrace: trap flag is not cleared after single step */
+ if (ctx->regs.eflags & 0x100) {
+ ctx->regs.eflags &= ~0x100;
+ ctx->regs_dirty = 1;
+ }
+#endif
+ if (ctx->regs_dirty) {
+ if (ptrace(PTRACE_SETREGS, ctx->pid, 0, &ctx->regs) < 0) {
+ int err = errno;
+#if USE_ESRCH_WORKAROUND
+ if (err == ESRCH) {
+ ctx->regs_dirty = 0;
+ ctx->stopped = 0;
+ event_context_started(ctx);
+ return 0;
+ }
+#endif
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_SETREGS) failed: ctx %#x, pid %d, error %d %s",
+ ctx, ctx->pid, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ ctx->regs_dirty = 0;
+ }
+ if (ptrace(PTRACE_CONT, ctx->pid, 0, signal) < 0) {
+ int err = errno;
+#if USE_ESRCH_WORKAROUND
+ if (err == ESRCH) {
+ ctx->stopped = 0;
+ event_context_started(ctx);
+ return 0;
+ }
+#endif
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_CONT, ...) failed: ctx %#x, pid %d, error %d %s",
+ ctx, ctx->pid, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ ctx->pending_signals &= ~(1 << signal);
+ ctx->stopped = 0;
+ event_context_started(ctx);
+ return 0;
+}
+
+int context_single_step(Context * ctx) {
+ assert(is_dispatch_thread());
+ assert(ctx->stopped);
+ assert(!ctx->pending_intercept);
+ assert(!ctx->pending_step);
+ assert(!ctx->exited);
+ trace(LOG_CONTEXT, "context: single step ctx %#x, pid %d", ctx, ctx->pid);
+ if (ctx->regs_dirty) {
+ if (ptrace(PTRACE_SETREGS, ctx->pid, 0, &ctx->regs) < 0) {
+ int err = errno;
+#if USE_ESRCH_WORKAROUND
+ if (err == ESRCH) {
+ ctx->regs_dirty = 0;
+ ctx->pending_step = 1;
+ ctx->stopped = 0;
+ event_context_started(ctx);
+ return 0;
+ }
+#endif
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_SETREGS) failed: ctx %#x, pid %d, error %d %s",
+ ctx, ctx->pid, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ ctx->regs_dirty = 0;
+ }
+ if (ptrace(PTRACE_SINGLESTEP, ctx->pid, 0, 0) < 0) {
+ int err = errno;
+#if USE_ESRCH_WORKAROUND
+ if (err == ESRCH) {
+ ctx->stopped = 0;
+ ctx->pending_step = 1;
+ event_context_started(ctx);
+ return 0;
+ }
+#endif
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_SINGLESTEP, ...) failed: ctx %#x, pid %d, error %d %s",
+ ctx, ctx->pid, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ ctx->pending_step = 1;
+ ctx->stopped = 0;
+ event_context_started(ctx);
+ return 0;
+}
+
+int context_write_mem(Context * ctx, unsigned long address, void * buf, size_t size) {
+ unsigned long word_addr;
+ assert(is_dispatch_thread());
+ assert(!ctx->exited);
+ assert(ctx->stopped);
+ trace(LOG_CONTEXT, "context: write memory ctx %#x, pid %d, address 0x%08x, size %d", ctx, ctx->pid, address, size);
+ for (word_addr = address & ~3ul; word_addr < address + size; word_addr += WORD_SIZE) {
+ int i;
+ unsigned int word = 0;
+ if (word_addr < address || word_addr + WORD_SIZE > address + size) {
+ errno = 0;
+ word = ptrace(PTRACE_PEEKDATA, ctx->pid, word_addr, 0);
+ if (errno != 0) {
+ int err = errno;
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_PEEKDATA, ...) failed: ctx %#x, pid %d, error %d %s",
+ ctx, ctx->pid, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ }
+ for (i = 0; i < WORD_SIZE; i++) {
+ if (word_addr + i >= address && word_addr + i < address + size) {
+ // TODO: big endian support
+ ((unsigned char *)&word)[i] = ((unsigned char *)buf)[word_addr + i - address];
+ }
+ }
+ if (ptrace(PTRACE_POKEDATA, ctx->pid, word_addr, word) < 0) {
+ int err = errno;
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_POKEDATA, ...) failed: ctx %#x, pid %d, error %d %s",
+ ctx, ctx->pid, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int context_read_mem(Context * ctx, unsigned long address, void * buf, size_t size) {
+ unsigned long word_addr;
+ assert(is_dispatch_thread());
+ assert(!ctx->exited);
+ assert(ctx->stopped);
+ trace(LOG_CONTEXT, "context: read memory ctx %#x, pid %d, address 0x%08x, size %d", ctx, ctx->pid, address, size);
+ for (word_addr = address & ~3ul; word_addr < address + size; word_addr += WORD_SIZE) {
+ int i;
+ unsigned int word = 0;
+ errno = 0;
+ word = ptrace(PTRACE_PEEKDATA, ctx->pid, word_addr, 0);
+ if (errno != 0) {
+ int err = errno;
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_PEEKDATA, ...) failed: ctx %#x, pid %d, error %d %s",
+ ctx, ctx->pid, err, errno_to_str(err));
+ errno = err;
+ return -1;
+ }
+ for (i = 0; i < WORD_SIZE; i++) {
+ if (word_addr + i >= address && word_addr + i < address + size) {
+ // TODO: big endian support
+ ((unsigned char *)buf)[word_addr + i - address] = ((unsigned char *)&word)[i];
+ }
+ }
+ }
+ return 0;
+}
+
+static void event_pid_exited(void *arg) {
+ struct pid_exit_info *eap = arg;
+ Context * ctx;
+
+ ctx = context_find_from_pid(eap->pid);
+ if (ctx == NULL) {
+ trace(LOG_EVENTS, "event: ctx not found, pid %d, exit status %d", eap->pid, eap->value);
+ }
+ else {
+ trace(LOG_EVENTS, "event: ctx %#x, pid %d, exit status %d", ctx, eap->pid, eap->value);
+ assert(!ctx->stopped);
+ assert(!ctx->intercepted);
+ assert(!ctx->exited);
+ /* Note: ctx->exiting should be 1 here. However, PTRACE_EVENT_EXIT can be lost by PTRACE because of racing
+ * between PTRACE_CONT and SIGTRAP/PTRACE_EVENT_EXIT. So, ctx->exiting can be 0.
+ */
+ ctx->exiting = 0;
+ ctx->exited = 1;
+ event_context_exited(ctx);
+ if (ctx->parent != NULL) {
+ list_remove(&ctx->cldl);
+ context_unlock(ctx->parent);
+ ctx->parent = NULL;
+ }
+ context_unlock(ctx);
+ }
+ loc_free(eap);
+}
+
+static void event_pid_stopped(void * arg) {
+ unsigned long msg = 0;
+ Context * ctx = NULL;
+ Context * ctx2 = NULL;
+ struct pid_stop_info * eap = arg;
+
+ trace(LOG_EVENTS, "event: pid %d stopped, signal %d, event %s",
+ eap->pid, eap->signal, event_name(eap->event));
+
+ ctx = context_find_from_pid(eap->pid);
+ if (ctx == NULL) {
+ trace(LOG_ALWAYS, "error: invalid event: pid %d is not traced", eap->pid);
+ return;
+ }
+ assert(!ctx->exited);
+ assert(!ctx->stopped || eap->event == 0 || eap->event == PTRACE_EVENT_EXIT);
+ if (ctx->trace_flags != PTRACE_FLAGS) {
+ if (ptrace(PTRACE_SETOPTIONS, ctx->pid, 0, PTRACE_FLAGS) < 0) {
+ int err = errno;
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_SETOPTIONS) failed: pid %d, error %d %s",
+ ctx->pid, err, errno_to_str(err));
+ }
+ else {
+ ctx->trace_flags = PTRACE_FLAGS;
+ }
+ }
+
+ switch (eap->event) {
+ case PTRACE_EVENT_FORK:
+ case PTRACE_EVENT_VFORK:
+ case PTRACE_EVENT_CLONE:
+ if (ptrace(PTRACE_GETEVENTMSG, eap->pid, 0, &msg) < 0) {
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_GETEVENTMSG) failed; pid %d, error %d %s",
+ eap->pid, errno, errno_to_str(errno));
+ break;
+ }
+ assert(msg != 0);
+ ctx2 = create_context(msg);
+ assert(ctx2->parent == NULL);
+ if (eap->event == PTRACE_EVENT_CLONE) {
+ ctx2->mem = ctx->mem;
+ ctx2->parent = ctx->parent != NULL ? ctx->parent : ctx;
+ ctx2->parent->ref_count++;
+ list_add_first(&ctx2->cldl, &ctx2->parent->children);
+ }
+ else {
+ ctx2->mem = ctx2->pid;
+ }
+ assert(ctx2->mem != 0);
+ event_context_created(ctx2);
+ break;
+
+ case PTRACE_EVENT_EXEC:
+ event_context_changed(ctx);
+ break;
+ }
+
+ if (eap->signal != SIGSTOP && eap->signal != SIGTRAP) {
+ ctx->pending_signals |= 1 << eap->signal;
+ }
+
+ if (eap->signal == SIGTRAP && eap->event == PTRACE_EVENT_EXIT) {
+ ctx->exiting = 1;
+ ctx->regs_dirty = 0;
+ }
+
+ if (!ctx->stopped || !ctx->intercepted) {
+ unsigned long pc0 = get_regs_PC(ctx->regs);
+ assert(!ctx->regs_dirty);
+ assert(!ctx->intercepted);
+ ctx->regs_error = 0;
+ if (ptrace(PTRACE_GETREGS, ctx->pid, 0, &ctx->regs) < 0) {
+#if USE_ESRCH_WORKAROUND
+ if (errno == ESRCH) {
+ /* Racing condition: somebody resumed this context while we are handling stop event.
+ *
+ * One possible cause: main thread has exited forcing children to exit too.
+ * I beleive it is a bug in PTRACE implementation - PTRACE should delay exiting of
+ * a context while it is stopped, but it does not, which causes a nasty racing.
+ *
+ * Workaround: Ignore current event, assume context is running.
+ */
+ loc_free(eap);
+ return;
+ }
+#endif
+ ctx->regs_error = errno;
+ trace(LOG_ALWAYS, "error: ptrace(PTRACE_GETREGS) failed; pid %d, error %d %s",
+ ctx->pid, errno, errno_to_str(errno));
+ }
+
+ trace(LOG_EVENTS, "event: pid %d stopped at PC = %d (0x%08x)",
+ ctx->pid, get_regs_PC(ctx->regs), get_regs_PC(ctx->regs));
+
+ if (eap->signal == SIGSTOP && ctx->pending_step && ctx->regs_error == 0 && pc0 == get_regs_PC(ctx->regs)) {
+ trace(LOG_EVENTS, "event: pid %d, single step failed because of pending SIGSTOP, retrying");
+ ptrace(PTRACE_SINGLESTEP, ctx->pid, 0, 0);
+ }
+ else {
+ ctx->signal = eap->signal;
+ ctx->event = eap->event;
+ ctx->pending_step = 0;
+ ctx->stopped = 1;
+ event_context_stopped(ctx);
+ }
+ }
+
+ loc_free(eap);
+}
+
+static void *wpid_handler(void *x) {
+ pid_t pid;
+ int status;
+ struct timeval timeout;
+
+ for (;;) {
+ attach_flag = 0;
+ if ((pid = waitpid(-1, &status, WUNTRACED | __WALL)) == (pid_t)-1) {
+ if (errno == ECHILD) {
+ pthread_mutex_lock(&waitpid_lock);
+ if (!attach_flag) pthread_cond_wait(&waitpid_cond, &waitpid_lock);
+ pthread_mutex_unlock(&waitpid_lock);
+ continue;
+ }
+ perror("waitpid");
+ exit(1);
+ }
+ trace(LOG_WAITPID, "waitpid: pid %d status %#x", pid, status);
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ struct pid_exit_info *eap;
+
+ eap = loc_alloc(sizeof *eap);
+ eap->pid = pid;
+ eap->value = WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status);
+ post_event(event_pid_exited, eap);
+ }
+ else if (WIFSTOPPED(status)) {
+ struct pid_stop_info *eap;
+
+ eap = loc_alloc(sizeof *eap);
+ eap->pid = pid;
+ eap->signal = WSTOPSIG(status);
+ eap->event = status >> 16;
+ post_event(event_pid_stopped, eap);
+ }
+ else {
+ trace(LOG_ALWAYS, "unexpected status (0x%x) from waitpid (pid %d)", status, pid);
+ }
+ }
+}
+
+static void init(void) {
+ pthread_mutex_init(&waitpid_lock, NULL);
+ pthread_cond_init(&waitpid_cond, NULL);
+ my_pid = getpid();
+ /* Create thread to get process events using waitpid() */
+ if (pthread_create(&wpid_thread, &pthread_create_attr, wpid_handler, NULL) != 0) {
+ perror("pthread_create");
+ exit(1);
+ }
+}
+
+#endif
+
+void add_context_event_listener(ContextEventListener * listener) {
+ listener->next = event_listeners;
+ event_listeners = listener;
+}
+
+void ini_contexts(void) {
+ int i;
+
+ list_init(&context_root);
+ for (i = 0; i < CONTEXT_PID_ROOT_SIZE; i++) {
+ list_init(&context_pid_root[i]);
+ }
+ init();
+}
diff --git a/context.h b/context.h
new file mode 100644
index 00000000..802e9f7d
--- /dev/null
+++ b/context.h
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module handles process/thread OS contexts and their state machine.
+ */
+
+#ifndef D_context
+#define D_context
+
+#include <sys/types.h>
+#include "mdep.h"
+#include "link.h"
+
+extern LINK context_root;
+
+#define ctxl2ctxp(A) ((Context *)((char *)(A) - (int)&((Context *)0)->ctxl))
+#define pidl2ctxp(A) ((Context *)((char *)(A) - (int)&((Context *)0)->pidl))
+#define cldl2ctxp(A) ((Context *)((char *)(A) - (int)&((Context *)0)->cldl))
+
+typedef struct Context Context;
+
+struct Context {
+ LINK ctxl;
+ LINK pidl;
+ LINK cldl;
+ LINK children;
+ Context * parent;
+ unsigned int ref_count; /* reference count, see context_lock() and context_unlock() */
+ pid_t pid; /* process or thread identifier */
+ pid_t mem; /* context memory space identifier */
+ int stopped; /* OS kernel has stopped this context */
+ int stopped_by_bp; /* stopped by breakpoint */
+ int exiting; /* context is about to exit */
+ int exited; /* context exited */
+ int intercepted; /* context is reported to a host as suspended */
+ int pending_step; /* context is executing single instruction step */
+ int pending_intercept; /* host is waiting for this context to be suspended */
+ int pending_safe_event; /* safe events are waiting for this context to be stopped */
+ unsigned long pending_signals; /* bitset of signals that were received, but not handled yet */
+ int signal; /* signal that stopped this context */
+ int event; /* tracing event code when signal is SIGTRAP */
+ REG_SET regs; /* copy of context registers, updated when context stops */
+ int regs_error; /* if not 0, 'regs' is invalid */
+ int regs_dirty; /* if not 0, 'regs' is modified and needs to be saved before context is continued */
+ void * stack_trace;
+ int trace_flags;
+#if defined(_WRS_KERNEL)
+ VXDBG_BP_INFO bp_info; /* breakpoint information */
+ pid_t bp_pid; /* process or thread that hit breakpoint */
+#endif
+};
+
+extern void ini_contexts(void);
+
+extern char * event_name(int event);
+extern char * signal_name(int signal);
+extern char * context_state_name(Context * ctx);
+
+/*
+ * Convert PID to TCF Context ID
+ */
+extern char * pid2id(pid_t pid, pid_t parent);
+
+/*
+ * Get context thread ID
+ */
+extern char * thread_id(Context * ctx);
+
+/*
+ * Get context container ID
+ */
+extern char * container_id(Context * ctx);
+
+/*
+ * Convert TCF Context ID to PID
+ */
+extern pid_t id2pid(char * id, pid_t * parent);
+
+/*
+ * Search Context record by TCF Context ID
+ */
+extern Context * id2ctx(char * id);
+
+/*
+ * Find a context by PID
+ */
+extern Context * context_find_from_pid(pid_t pid);
+
+/*
+ * Start tracing of a process.
+ */
+extern int context_attach(pid_t pid, Context ** ctx);
+
+/*
+ * Increment reference counter of Context object.
+ * While ref count > 0 object will not be deleted even when context exits.
+ */
+extern void context_lock(Context * ctx);
+
+/*
+ * Decrement reference counter.
+ * If ref count == 0, delete Context object.
+ */
+extern void context_unlock(Context * ctx);
+
+extern int context_stop(Context * ctx);
+extern int context_continue(Context * ctx);
+extern int context_single_step(Context * ctx);
+extern int context_write_mem(Context * ctx, unsigned long address, void * buf, size_t size);
+extern int context_read_mem(Context * ctx, unsigned long address, void * buf, size_t size);
+
+typedef struct ContextEventListener {
+ void (*context_created)(Context * ctx);
+ void (*context_exited)(Context * ctx);
+ void (*context_stopped)(Context * ctx);
+ void (*context_started)(Context * ctx);
+ void (*context_changed)(Context * ctx);
+ struct ContextEventListener * next;
+} ContextEventListener;
+
+extern void add_context_event_listener(ContextEventListener * listener);
+
+#ifdef _WRS_KERNEL
+extern VXDBG_CLNT_ID vxdbg_clnt_id;
+#endif
+
+#endif
diff --git a/diagnostics.c b/diagnostics.c
new file mode 100644
index 00000000..85ac3bd4
--- /dev/null
+++ b/diagnostics.c
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Diagnostics service.
+ * This service is used for framework and agents testing.
+ */
+
+#include "config.h"
+#if defined(_WRS_KERNEL)
+# include <vxWorks.h>
+#endif
+#include <signal.h>
+#include <assert.h>
+#include <stdio.h>
+#include "diagnostics.h"
+#include "protocol.h"
+#include "json.h"
+#include "exceptions.h"
+#include "runctrl.h"
+#include "symbols.h"
+#include "test.h"
+
+static const char * DIAGNOSTICS = "Diagnostics";
+
+static void command_echo(char * token, InputStream * inp, OutputStream * out) {
+ char str[0x1000];
+ int len = json_read_string(inp, str, sizeof(str));
+ if (len >= sizeof(str)) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ json_write_string_len(out, str, len);
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_test_list(char * token, InputStream * inp, OutputStream * out) {
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+#if defined(WIN32)
+ write_stringz(out, "[]");
+#elif defined(_WRS_KERNEL)
+ write_stringz(out, "[\"RCBP1\"]");
+#else
+ write_stringz(out, "[\"RCBP1\"]");
+#endif
+ out->write(out, MARKER_EOM);
+}
+
+static void command_run_test(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ pid_t pid = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (strcmp(id, "RCBP1") == 0) {
+ if (run_test_process(&pid) < 0) err = errno;
+ }
+ else {
+ err = EINVAL;
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ json_write_string(out, err ? NULL : pid2id(pid, 0));
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void event_terminate(void * arg) {
+ Context * ctx = arg;
+ LINK * qp = ctx->children.next;
+ while (qp != &ctx->children) {
+ cldl2ctxp(qp)->pending_signals |= 1 << SIGKILL;
+ qp = qp->next;
+ }
+ ctx->pending_signals |= 1 << SIGKILL;
+ context_unlock(ctx);
+}
+
+static void command_cancel_test(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ Context * ctx = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+#if SERVICE_RunControl
+ ctx = id2ctx(id);
+ if (ctx != NULL && !ctx->exited) {
+ context_lock(ctx);
+ post_safe_event(event_terminate, ctx);
+ }
+#endif
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_symbol(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ char name[0x1000];
+ Context * ctx;
+ Symbol sym;
+ int error = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ json_read_string(inp, name, sizeof(name));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+#if SERVICE_RunControl && SERVICE_Symbols
+ ctx = id2ctx(id);
+ if (ctx == NULL || ctx->exited) {
+ error = ERR_INV_CONTEXT;
+ }
+ else if (find_symbol(ctx, name, &sym) < 0) {
+ error = errno;
+ }
+#else
+ ctx = NULL;
+ error = EINVAL;
+#endif
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, error);
+ if (error != 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ out->write(out, '{');
+ json_write_string(out, "Abs");
+ out->write(out, ':');
+ json_write_boolean(out, sym.abs);
+ out->write(out, ',');
+ json_write_string(out, "Value");
+ out->write(out, ':');
+ json_write_ulong(out, sym.value);
+ if (sym.section != NULL) {
+ out->write(out, ',');
+ json_write_string(out, "Section");
+ out->write(out, ':');
+ json_write_string(out, sym.section);
+ }
+ if (sym.storage != NULL) {
+ out->write(out, ',');
+ json_write_string(out, "Storage");
+ out->write(out, ':');
+ json_write_string(out, sym.storage);
+ }
+ out->write(out, '}');
+ out->write(out, 0);
+ }
+ out->write(out, MARKER_EOM);
+}
+
+void ini_diagnostics_service(void) {
+ add_command_handler(DIAGNOSTICS, "echo", command_echo);
+ add_command_handler(DIAGNOSTICS, "getTestList", command_get_test_list);
+ add_command_handler(DIAGNOSTICS, "runTest", command_run_test);
+ add_command_handler(DIAGNOSTICS, "cancelTest", command_cancel_test);
+ add_command_handler(DIAGNOSTICS, "getSymbol", command_get_symbol);
+}
+
diff --git a/diagnostics.h b/diagnostics.h
new file mode 100644
index 00000000..09f6e09f
--- /dev/null
+++ b/diagnostics.h
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Diagnostics service.
+ * This service is used for framework and agents testing.
+ */
+
+#ifndef D_diagnostics
+#define D_diagnostics
+
+extern void ini_diagnostics_service(void);
+
+#endif
diff --git a/dwarf.h b/dwarf.h
new file mode 100644
index 00000000..e5dace74
--- /dev/null
+++ b/dwarf.h
@@ -0,0 +1,563 @@
+/*******************************************************************************
+ * Copyright (c) 1996 - 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
+ *******************************************************************************/
+
+/*
+ * DWARF Debugging Information Format.
+ */
+
+#define TAG_padding 0x0000
+#define TAG_array_type 0x0001
+#define TAG_class_type 0x0002
+#define TAG_entry_point 0x0003
+#define TAG_enumeration_type 0x0004
+#define TAG_formal_parameter 0x0005
+#define TAG_global_subroutine 0x0006
+#define TAG_global_variable 0x0007
+#define TAG_imported_declaration 0x0008
+#define TAG_label 0x000a
+#define TAG_lexical_block 0x000b
+#define TAG_local_variable 0x000c
+#define TAG_member 0x000d
+#define TAG_pointer_type 0x000f
+#define TAG_reference_type 0x0010
+#define TAG_compile_unit 0x0011
+#define TAG_source_file 0x0011
+#define TAG_string_type 0x0012
+#define TAG_structure_type 0x0013
+#define TAG_subroutine 0x0014
+#define TAG_subroutine_type 0x0015
+#define TAG_typedef 0x0016
+#define TAG_union_type 0x0017
+#define TAG_unspecified_parameters 0x0018
+#define TAG_variant 0x0019
+#define TAG_common_block 0x001a
+#define TAG_common_inclusion 0x001b
+#define TAG_inheritance 0x001c
+#define TAG_inlined_subroutine 0x001d
+#define TAG_module 0x001e
+#define TAG_ptr_to_member_type 0x001f
+#define TAG_set_type 0x0020
+#define TAG_subrange_type 0x0021
+#define TAG_with_stmt 0x0022
+#define TAG_access_declaration 0x0023
+#define TAG_base_type 0x0024
+#define TAG_catch_block 0x0025
+#define TAG_const_type 0x0026
+#define TAG_constant 0x0027
+#define TAG_enumerator 0x0028
+#define TAG_file_type 0x0029
+#define TAG_friend 0x002a
+#define TAG_namelist 0x002b
+#define TAG_namelist_item 0x002c
+#define TAG_packed_type 0x002d
+#define TAG_subprogram 0x002e
+#define TAG_template_type_param 0x002f
+#define TAG_template_value_param 0x0030
+#define TAG_thrown_type 0x0031
+#define TAG_try_block 0x0032
+#define TAG_variant_part 0x0033
+#define TAG_variable 0x0034
+#define TAG_volatile_type 0x0035
+#define TAG_dwarf_procedure 0x0036
+#define TAG_restrict_type 0x0037
+#define TAG_interface_type 0x0038
+#define TAG_namespace 0x0039
+#define TAG_imported_module 0x003a
+#define TAG_unspecified_type 0x003b
+#define TAG_partial_unit 0x003c
+#define TAG_imported_unit 0x003d
+#define TAG_mutable_type 0x003e
+#define TAG_condition 0x003f
+#define TAG_shared_type 0x0040
+#define TAG_lo_user 0x4080
+#define TAG_wrs_thrown_object 0x4080
+#define TAG_wrs_throw_breakpoint 0x4081
+#define TAG_wrs_catch_breakpoint 0x4082
+#define TAG_wrs_extern_subroutine 0x4083
+#define TAG_hi_user 0xffff
+
+#define CHILDREN_no 0x00
+#define CHILDREN_yes 0x01
+
+#define FORM_ADDR 0x0001
+#define FORM_REF 0x0002
+#define FORM_BLOCK2 0x0003
+#define FORM_BLOCK4 0x0004
+#define FORM_DATA2 0x0005
+#define FORM_DATA4 0x0006
+#define FORM_DATA8 0x0007
+#define FORM_STRING 0x0008
+#define FORM_BLOCK 0x0009
+#define FORM_BLOCK1 0x000a
+#define FORM_DATA1 0x000b
+#define FORM_FLAG 0x000c
+#define FORM_SDATA 0x000d
+#define FORM_STRP 0x000e
+#define FORM_UDATA 0x000f
+#define FORM_REF_ADDR 0x0010
+#define FORM_REF1 0x0011
+#define FORM_REF2 0x0012
+#define FORM_REF4 0x0013
+#define FORM_REF8 0x0014
+#define FORM_REF_UDATA 0x0015
+#define FORM_INDIRECT 0x0016
+
+#define AT_sibling 0x0001
+#define AT_location 0x0002
+#define AT_name 0x0003
+#define AT_fund_type 0x0005
+#define AT_mod_fund_type 0x0006
+#define AT_user_def_type 0x0007
+#define AT_mod_u_d_type 0x0008
+#define AT_ordering 0x0009
+#define AT_subscr_data 0x000a
+#define AT_byte_size 0x000b
+#define AT_bit_offset 0x000c
+#define AT_bit_size 0x000d
+#define AT_element_list 0x000f
+#define AT_stmt_list 0x0010
+#define AT_low_pc 0x0011
+#define AT_high_pc 0x0012
+#define AT_language 0x0013
+#define AT_member 0x0014
+#define AT_discr 0x0015
+#define AT_discr_value 0x0016
+#define AT_visibility 0x0017
+#define AT_import 0x0018
+#define AT_string_length 0x0019
+#define AT_common_reference 0x001a
+#define AT_comp_dir 0x001b
+#define AT_const_value 0x001c
+#define AT_constaining_type 0x001d
+#define AT_default_value 0x001e
+#define AT_friends 0x001f
+#define AT_inline 0x0020
+#define AT_is_optional 0x0021
+#define AT_lower_bound 0x0022
+#define AT_program 0x0023
+#define AT_private 0x0024
+#define AT_producer 0x0025
+#define AT_protected 0x0026
+#define AT_prototyped 0x0027
+#define AT_public 0x0028
+#define AT_pure_virtual 0x0029
+#define AT_return_addr 0x002a
+#define AT_specification_v1 0x002b
+#define AT_start_scope 0x002c
+#define AT_stride_size 0x002e
+#define AT_upper_bound 0x002f
+#define AT_virtual 0x0030
+#define AT_abstract_origin 0x0031
+#define AT_accessibility 0x0032
+#define AT_address_class 0x0033
+#define AT_artificial 0x0034
+#define AT_base_types 0x0035
+#define AT_calling_convention 0x0036
+#define AT_count 0x0037
+#define AT_data_member_location 0x0038
+#define AT_decl_column 0x0039
+#define AT_decl_file 0x003a
+#define AT_decl_line 0x003b
+#define AT_declaration 0x003c
+#define AT_distr_list 0x003d
+#define AT_encoding 0x003e
+#define AT_external 0x003f
+#define AT_frame_base 0x0040
+#define AT_friend 0x0041
+#define AT_identifier_case 0x0042
+#define AT_macro_info 0x0043
+#define AT_namelist_info 0x0044 /* typo? item */
+#define AT_priority 0x0045
+#define AT_segment 0x0046
+#define AT_specification_v2 0x0047 /* v2 */
+#define AT_static_link 0x0048
+#define AT_type 0x0049
+#define AT_use_location 0x004a
+#define AT_variable_parameter 0x004b
+#define AT_virtuality 0x004c
+#define AT_vtable_elem_location 0x004d
+#define AT_allocated 0x004e /* v3 */
+#define AT_associated 0x004f /* v3 */
+#define AT_mangled 0x0050 /* v1 */
+#define AT_data_location 0x0050 /* v2 */
+#define AT_stride 0x0051 /* v3 */
+#define AT_entry_pc 0x0052 /* v3 */
+#define AT_use_UTF8 0x0053 /* v3 */
+#define AT_extension 0x0054 /* v3 */
+#define AT_ranges 0x0055 /* v3 */
+#define AT_trampoline 0x0056 /* v3 */
+#define AT_call_column 0x0057 /* v3 */
+#define AT_call_file 0x0058 /* v3 */
+#define AT_call_line 0x0059 /* v3 */
+#define AT_description 0x005a /* v3 */
+#define AT_lo_user_v1 0x0200
+#define AT_hi_user_v1 0x03ff
+#define AT_push_mask 0x0220
+#define AT_frame_size 0x0221
+#define AT_main_unit 0x0222
+#define AT_stack_use 0x0223
+#define AT_source_file_names 0x0800
+#define AT_source_info 0x0810
+#define AT_lo_user_v2 0x2000
+#define AT_wrs_options 0x2001
+#define AT_hi_user_v2 0x3fff
+
+
+#define OP_reg 0x01 /* v1 */
+#define OP_basereg 0x02 /* v1 */
+#define OP_addr 0x03
+#define OP_const 0x04 /* v1 */
+#define OP_deref2 0x05 /* v1 */
+#define OP_deref 0x06
+#define OP_add 0x07 /* v1 */
+#define OP_const1u 0x08
+#define OP_const1s 0x09
+#define OP_const2u 0x0a
+#define OP_const2s 0x0b
+#define OP_const4u 0x0c
+#define OP_const4s 0x0d
+#define OP_const8u 0x0e
+#define OP_const8s 0x0f
+#define OP_constu 0x10
+#define OP_consts 0x11
+#define OP_dup 0x12
+#define OP_drop 0x13
+#define OP_over 0x14
+#define OP_pick 0x15
+#define OP_swap 0x16
+#define OP_rot 0x17
+#define OP_xderef 0x18
+#define OP_abs 0x19
+#define OP_and 0x1a
+#define OP_div 0x1b
+#define OP_minus 0x1c
+#define OP_mod 0x1d
+#define OP_mul 0x1e
+#define OP_neg 0x1f
+#define OP_not 0x20
+#define OP_or 0x21
+#define OP_plus 0x22
+#define OP_plus_uconst 0x23
+#define OP_shl 0x24
+#define OP_shr 0x25
+#define OP_shra 0x26
+#define OP_xor 0x27
+#define OP_bra 0x28
+#define OP_eq 0x29
+#define OP_ge 0x2a
+#define OP_gt 0x2b
+#define OP_le 0x2c
+#define OP_lt 0x2d
+#define OP_ne 0x2e
+#define OP_skip 0x2f
+#define OP_lit0 0x30
+#define OP_lit1 0x31
+#define OP_lit2 0x32
+#define OP_lit3 0x33
+#define OP_lit4 0x34
+#define OP_lit5 0x35
+#define OP_lit6 0x36
+#define OP_lit7 0x37
+#define OP_lit8 0x38
+#define OP_lit9 0x39
+#define OP_lit10 0x3a
+#define OP_lit11 0x3b
+#define OP_lit12 0x3c
+#define OP_lit13 0x3d
+#define OP_lit14 0x3e
+#define OP_lit15 0x3f
+#define OP_lit16 0x40
+#define OP_lit17 0x41
+#define OP_lit18 0x42
+#define OP_lit19 0x43
+#define OP_lit20 0x44
+#define OP_lit21 0x45
+#define OP_lit22 0x46
+#define OP_lit23 0x47
+#define OP_lit24 0x48
+#define OP_lit25 0x49
+#define OP_lit26 0x4a
+#define OP_lit27 0x4b
+#define OP_lit28 0x4c
+#define OP_lit29 0x4d
+#define OP_lit30 0x4e
+#define OP_lit31 0x4f
+#define OP_reg0 0x50
+#define OP_reg1 0x51
+#define OP_reg2 0x52
+#define OP_reg3 0x53
+#define OP_reg4 0x54
+#define OP_reg5 0x55
+#define OP_reg6 0x56
+#define OP_reg7 0x57
+#define OP_reg8 0x58
+#define OP_reg9 0x59
+#define OP_reg10 0x5a
+#define OP_reg11 0x5b
+#define OP_reg12 0x5c
+#define OP_reg13 0x5d
+#define OP_reg14 0x5e
+#define OP_reg15 0x5f
+#define OP_reg16 0x60
+#define OP_reg17 0x61
+#define OP_reg18 0x62
+#define OP_reg19 0x63
+#define OP_reg20 0x64
+#define OP_reg21 0x65
+#define OP_reg22 0x66
+#define OP_reg23 0x67
+#define OP_reg24 0x68
+#define OP_reg25 0x69
+#define OP_reg26 0x6a
+#define OP_reg27 0x6b
+#define OP_reg28 0x6c
+#define OP_reg29 0x6d
+#define OP_reg30 0x6e
+#define OP_reg31 0x6f
+#define OP_breg0 0x70
+#define OP_breg1 0x71
+#define OP_breg2 0x72
+#define OP_breg3 0x73
+#define OP_breg4 0x74
+#define OP_breg5 0x75
+#define OP_breg6 0x76
+#define OP_breg7 0x77
+#define OP_breg8 0x78
+#define OP_breg9 0x79
+#define OP_breg10 0x7a
+#define OP_breg11 0x7b
+#define OP_breg12 0x7c
+#define OP_breg13 0x7d
+#define OP_breg14 0x7e
+#define OP_breg15 0x7f
+#define OP_breg16 0x80
+#define OP_breg17 0x81
+#define OP_breg18 0x82
+#define OP_breg19 0x83
+#define OP_breg20 0x84
+#define OP_breg21 0x85
+#define OP_breg22 0x86
+#define OP_breg23 0x87
+#define OP_breg24 0x88
+#define OP_breg25 0x89
+#define OP_breg26 0x8a
+#define OP_breg27 0x8b
+#define OP_breg28 0x8c
+#define OP_breg29 0x8d
+#define OP_breg30 0x8e
+#define OP_breg31 0x8f
+#define OP_regx 0x90
+#define OP_fbreg 0x91
+#define OP_bregx 0x92
+#define OP_piece 0x93
+#define OP_deref_size 0x94
+#define OP_xderef_size 0x95
+#define OP_nop 0x96
+#define OP_push_object_address 0x97
+#define OP_call2 0x98
+#define OP_call4 0x99
+#define OP_calli 0x9a /* typo? */
+#define OP_ref 0x9a
+#define OP_call_ref 0x9a
+#define OP_bit_piece 0x9d
+#define OP_lo_user 0xe0
+#define OP_hi_user 0xff
+
+#define FT_char 0x0001
+#define FT_signed_char 0x0002
+#define FT_unsigned_char 0x0003
+#define FT_short 0x0004
+#define FT_signed_short 0x0005
+#define FT_unsigned_short 0x0006
+#define FT_integer 0x0007
+#define FT_signed_integer 0x0008
+#define FT_unsigned_integer 0x0009
+#define FT_long 0x000a
+#define FT_signed_long 0x000b
+#define FT_unsigned_long 0x000c
+#define FT_pointer 0x000d
+#define FT_float 0x000e
+#define FT_dbl_prec_float 0x000f
+#define FT_ext_prec_float 0x0010
+#define FT_complex 0x0011
+#define FT_dbl_prec_complex 0x0012
+#define FT_void 0x0014
+#define FT_boolean 0x0015
+#define FT_ext_prec_complex 0x0016
+#define FT_label 0x0017
+#define FT_lo_user 0x8000
+#define FT_hi_user 0xffff
+#define FT_longlong 0x8008
+#define FT_signed_longlong 0x8108
+#define FT_unsigned_longlong 0x8208
+#define FT_vector_signed_char 0xa002
+#define FT_vector_unsigned_char 0xa003
+#define FT_vector_signed_short 0xa005
+#define FT_vector_unsigned_short 0xa006
+#define FT_vector_signed_int 0xa008
+#define FT_vector_unsigned_int 0xa009
+#define FT_vector_float 0xa00e
+#define FT_ev64_s16 0xb005
+#define FT_ev64_u16 0xb006
+#define FT_ev64_s32 0xb008
+#define FT_ev64_u32 0xb009
+#define FT_ev64_s64 0xb208
+#define FT_ev64_u64 0xb209
+#define FT_ev64_fs 0xb00e
+#define FT_ev64_opaque 0xb020
+
+#define MOD_pointer_to 0x01
+#define MOD_reference_to 0x02
+#define MOD_const 0x03
+#define MOD_volatile 0x04
+#define MOD_lo_user 0x80
+#define MOD_hi_user 0xff
+
+#define LANG_C89 0x00000001
+#define LANG_C 0x00000002
+#define LANG_ADA83 0x00000003
+#define LANG_C_PLUS_PLUS 0x00000004
+#define LANG_COBOL74 0x00000005
+#define LANG_COBOL85 0x00000006
+#define LANG_FORTRAN77 0x00000007
+#define LANG_FORTRAN90 0x00000008
+#define LANG_PASCAL83 0x00000009
+#define LANG_MODULA2 0x0000000a
+#define LANG_JAVA 0x0000000b /* v3 */
+#define LANG_C99 0x0000000c /* v3 */
+#define LANG_ADA95 0x0000000d /* v3 */
+#define LANG_FORTRAN95 0x0000000e /* v3 */
+#define LANG_PLI 0x0000000f
+#define LANG_lo_user 0x00008000
+#define LANG_hi_user 0x0000ffff
+
+#define ORD_row_major 0
+#define ORD_col_major 1
+
+#define FMT_FT_C_C 0x0
+#define FMT_FT_C_X 0x1
+#define FMT_FT_X_C 0x2
+#define FMT_FT_X_X 0x3
+#define FMT_UT_C_C 0x4
+#define FMT_UT_C_X 0x5
+#define FMT_UT_X_C 0x6
+#define FMT_UT_X_X 0x7
+#define FMT_ET 0x8
+
+#define DW_ATE_address 0x01
+#define DW_ATE_boolean 0x02
+#define DW_ATE_complex_float 0x03
+#define DW_ATE_float 0x04
+#define DW_ATE_signed 0x05
+#define DW_ATE_signed_char 0x06
+#define DW_ATE_unsigned 0x07
+#define DW_ATE_unsigned_char 0x08
+#define DW_ATE_imaginary_float 0x09 /* v3 */
+#define DW_ATE_lo_user 0x80
+#define DW_ATE_hi_user 0xff
+
+#define DW_LNS_copy 1
+#define DW_LNS_advance_pc 2
+#define DW_LNS_advance_line 3
+#define DW_LNS_set_file 4
+#define DW_LNS_set_column 5
+#define DW_LNS_negate_stmt 6
+#define DW_LNS_set_basic_block 7
+#define DW_LNS_const_add_pc 8
+#define DW_LNS_fixed_advance_pc 9
+#define DW_LNS_set_prologue_end 0xa /* v3 */
+#define DW_LNS_set_epilogue_begin 0xb /* v3 */
+#define DW_LNS_set_isa 0xc /* v3 */
+#define DW_LNS_expected_opcode_base 0xd /* highest standard opcode plus one */
+#define DW_LNS_vendor_extension 0x100
+#define DW_LNS_special_opcode 0x101
+
+#define DW_LNE_end_sequence 1
+#define DW_LNE_set_address 2
+#define DW_LNE_define_file 3
+#define DW_LNE_lo_user 0x80 /* v3 */
+#define DW_LNE_hi_user 0xff /* v3 */
+
+#define ACCESS_public 1
+#define ACCESS_protected 2
+#define ACCESS_private 3
+
+#define VIS_local 1
+#define VIS_exported 2
+#define VIS_qualified 3
+
+#define VIRTUALITY_none 0
+#define VIRTUALITY_virtual 1
+#define VIRTUALITY_pure_virtual 2
+
+#define ID_case_sensitive 0
+#define ID_up_case 1
+#define ID_down_case 2
+#define ID_case_insensitive 3
+
+#define CC_normal 0x01
+#define CC_program 0x02
+#define CC_nocall 0x03
+#define CC_lo_user 0x40
+#define CC_hi_user 0xff
+
+#define INL_not_inlined 0
+#define INL_inlined 1
+#define INL_declared_not_inlined 2
+#define INL_declared_inlined 3
+
+#define DSC_label 0
+#define DSC_range 1
+
+#define MACINFO_define 1
+#define MACINFO_undef 2
+#define MACINFO_start_file 3
+#define MACINFO_end_file 4
+#define MACINFO_vendor_ext 0xff
+
+/* The following three defines represent */
+/* the high 2 bits only. */
+#define CFA_advance_loc 0x01
+#define CFA_offset 0x02
+#define CFA_restore 0x03
+
+#define CFA_nop 0x00
+#define CFA_set_loc 0x01
+#define CFA_advance_loc1 0x02
+#define CFA_advance_loc2 0x03
+#define CFA_advance_loc4 0x04
+#define CFA_offset_extended 0x05
+#define CFA_restore_extended 0x06
+#define CFA_undefined 0x07
+#define CFA_same_value 0x08
+#define CFA_register 0x09
+#define CFA_remember_state 0x0a
+#define CFA_restore_state 0x0b
+#define CFA_def_cfa 0x0c
+#define CFA_def_cfa_register 0x0d
+#define CFA_def_cfa_offset 0x0e
+#define CFA_def_cfa_expression 0x0f
+#define CFA_expression 0x10 /* v3 */
+#define CFA_offset_extended_sf 0x11 /* v3 */
+#define CFA_def_cfa_sf 0x12 /* v3 */
+#define CFA_def_cfa_offset_sf 0x13 /* v3 */
+#define CFA_lo_user 0x1c
+#define CFA_hi_user 0x3f
+
+
+#define ADDR_none 0
+#define ADDR_near16 1
+#define ADDR_far16 2
+#define ADDR_huge16 3
+#define ADDR_near32 4
+#define ADDR_far32 5
+
+
diff --git a/dwarfio.c b/dwarfio.c
new file mode 100644
index 00000000..7b55d190
--- /dev/null
+++ b/dwarfio.c
@@ -0,0 +1,728 @@
+/*******************************************************************************
+ * Copyright (c) 2006-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
+ *******************************************************************************/
+
+/*
+ * This module implements low-level functions for reading DWARF debug information.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+#include "config.h"
+#if SERVICE_LineNumbers
+
+#include <assert.h>
+#include <string.h>
+#include "dwarfio.h"
+#include "dwarf.h"
+#include "myalloc.h"
+#include "exceptions.h"
+
+#define INP_BUF_SIZE 0x1000
+#define ABBREV_TABLE_SIZE 1021
+
+struct DIO_Abbreviation {
+ U2_T mTag;
+ U1_T mChildren;
+ U4_T mAttrLen;
+ U2_T mAttrs[2];
+};
+
+typedef struct DIO_Abbreviation DIO_Abbreviation;
+
+struct DIO_AbbrevSet {
+ struct DIO_AbbrevSet * mNext;
+ ELF_File * mFile;
+ U8_T mOffset;
+ DIO_Abbreviation ** mTable;
+ U4_T mSize;
+};
+
+typedef struct DIO_AbbrevSet DIO_AbbrevSet;
+
+struct DIO_Cache {
+ U1_T * mStringTable;
+ U4_T mStringTableSize;
+ DIO_AbbrevSet ** mAbbrevTable;
+};
+
+typedef struct DIO_Cache DIO_Cache;
+
+U4_T dio_gVersion = 0;
+U1_T dio_g64bit = 0;
+U1_T dio_gAddressSize = 4;
+U8_T dio_gUnitPos = 0;
+U4_T dio_gUnitSize = 0;
+U8_T dio_gEntryPos = 0;
+
+U8_T dio_gFormRef;
+U8_T dio_gFormData = 0;
+U1_T dio_gFormDataSize = 0;
+U4_T dio_gFormBlockSize = 0;
+U1_T * dio_gFormBlockBuf = NULL;
+
+static ELF_Section * sSection;
+static U1_T sBigEndian;
+static DIO_Abbreviation ** sAbbrevTable = NULL;
+static U4_T sAbbrevTableSize = 0;
+static U1_T * sInpBuf = NULL;
+static U4_T sBufPos;
+static U4_T sBufLen;
+static U8_T sDataPos;
+
+static void dio_CloseELF(ELF_File * File) {
+ U4_T n, m;
+ DIO_Cache * Cache = (DIO_Cache *)File->dwarf_cache;
+
+ if (Cache == NULL) return;
+ for (n = 0; n < ABBREV_TABLE_SIZE; n++) {
+ DIO_AbbrevSet * Set = Cache->mAbbrevTable[n];
+ while (Set != NULL) {
+ DIO_AbbrevSet * Next = Set->mNext;
+ for (m = 0; m < Set->mSize; m++) {
+ loc_free(Set->mTable[m]);
+ }
+ loc_free(Set->mTable);
+ loc_free(Set);
+ Set = Next;
+ }
+ }
+ loc_free(Cache->mAbbrevTable);
+ File->dwarf_cache = NULL;
+}
+
+static DIO_Cache * dio_GetCache(ELF_File * File) {
+ static int Inited = 0;
+ DIO_Cache * Cache = (DIO_Cache *)File->dwarf_cache;
+
+ if (!Inited) {
+ elf_add_close_listener(dio_CloseELF);
+ Inited = 1;
+ }
+ if (Cache == NULL) {
+ Cache = (DIO_Cache *)(File->dwarf_cache = loc_alloc_zero(sizeof(DIO_Cache)));
+ }
+ return Cache;
+}
+
+void dio_EnterSection(ELF_Section * Section, U8_T Offset) {
+ sSection = Section;
+ sDataPos = Offset;
+ sBufPos = 0;
+ sBufLen = 0;
+ sBigEndian = Section->file->big_endian;
+
+ if (sInpBuf == NULL) {
+ sInpBuf = (U1_T *)loc_alloc(INP_BUF_SIZE);
+ }
+}
+
+void dio_ExitSection() {
+ sSection = NULL;
+ sDataPos = 0;
+ sBufPos = 0;
+ sBufLen = 0;
+}
+
+U8_T dio_GetPos() {
+ return sDataPos;
+}
+
+void dio_Skip(U8_T Bytes) {
+ sDataPos += Bytes;
+ if (sBufPos + Bytes >= sBufLen) {
+ sBufPos = 0;
+ sBufLen = 0;
+ }
+ else {
+ sBufPos += (U4_T)Bytes;
+ }
+}
+
+void dio_Read(U1_T * Buf, U4_T Size) {
+ assert(sSection->size >= sDataPos + Size);
+ if (sBufPos < sBufLen) {
+ U4_T FromBuf = Size < sBufLen - sBufPos ? Size : sBufLen - sBufPos;
+ memcpy(Buf, sInpBuf + sBufPos, FromBuf);
+ sBufPos += FromBuf;
+ Buf += FromBuf;
+ Size -= FromBuf;
+ sDataPos += FromBuf;
+ }
+ if (Size > 0) {
+ U4_T Rd = 0;
+ if (elf_read(sSection, sDataPos, Buf, Size, &Rd) < 0) exception(errno);
+ if (Rd < Size) exception(ERR_EOF);
+ assert(sBufPos >= sBufLen);
+ sDataPos += Size;
+ }
+}
+
+U1_T dio_ReadU1F(void) {
+ U1_T c;
+ if (sDataPos >= sSection->size) exception(ERR_EOF);
+ if (sBufPos < sBufLen) {
+ c = sInpBuf[sBufPos++];
+ }
+ else if (sBufPos >= sBufLen) {
+ U4_T Rd = 0;
+ U8_T Size = sSection->size - sDataPos;
+ if (Size > INP_BUF_SIZE) Size = INP_BUF_SIZE;
+ if (elf_read(sSection, sDataPos, sInpBuf, (U4_T)Size, &Rd) < 0) exception(errno);
+ if (Rd == 0) exception(ERR_EOF);
+ sBufLen = Rd;
+ c = sInpBuf[0];
+ sBufPos = 1;
+ }
+ sDataPos++;
+ return c;
+}
+
+U1_T dio_ReadU1(void) {
+ return sBufPos >= sBufLen ? dio_ReadU1F() : (sDataPos++, sInpBuf[sBufPos++]);
+}
+
+#define dio_ReadU1() (sBufPos >= sBufLen ? dio_ReadU1F() : (sDataPos++, sInpBuf[sBufPos++]))
+
+U2_T dio_ReadU2(void) {
+ U1_T x0 = dio_ReadU1();
+ U1_T x1 = dio_ReadU1();
+ return sBigEndian ? (x0 << 8) | x1 : x0 | (x1 << 8);
+}
+
+U4_T dio_ReadU4(void) {
+ U2_T x0 = dio_ReadU2();
+ U2_T x1 = dio_ReadU2();
+ return sBigEndian ? (x0 << 16) | x1 : x0 | (x1 << 16);
+}
+
+U8_T dio_ReadU8(void) {
+ U8_T x0 = dio_ReadU4();
+ U8_T x1 = dio_ReadU4();
+ return sBigEndian ? (x0 << 32) | x1 : x0 | (x1 << 32);
+}
+
+U4_T dio_ReadLEB128(void) {
+ U4_T res = 0;
+ int i = 0;
+ for (;; i += 7) {
+ U1_T n = dio_ReadU1();
+ res |= (n & 0x7Fu) << i;
+ if ((n & 0x80) == 0) break;
+ }
+ return res;
+}
+
+U8_T dio_ReadU8LEB128(void) {
+ U8_T res = 0;
+ int i = 0;
+ for (;; i += 7) {
+ U1_T n = dio_ReadU1();
+ res |= (n & 0x7Fu) << i;
+ if ((n & 0x80) == 0) break;
+ }
+ return res;
+}
+
+I8_T dio_ReadI8LEB128(void) {
+ U8_T res = 0;
+ int i = 0;
+ for (;; i += 7) {
+ U1_T n = dio_ReadU1();
+ res |= (n & 0x7Fu) << i;
+ if ((n & 0x80) == 0) {
+ res |= -(n & 0x40) << i;
+ break;
+ }
+ }
+ return (I8_T)res;
+}
+
+U8_T dio_ReadUX(int Size) {
+ switch (Size) {
+ case 2:
+ return dio_ReadU2();
+ case 4:
+ return dio_ReadU4();
+ case 8:
+ return dio_ReadU8();
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+U8_T dio_ReadAddress(void) {
+ switch (dio_gAddressSize) {
+ case 2:
+ return dio_ReadU2();
+ case 4:
+ return dio_ReadU4();
+ case 8:
+ return dio_ReadU8();
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+static void dio_CheckBlockBufCapacity(U4_T Size) {
+ static U4_T BufSize = 0;
+ if (BufSize < Size) {
+ BufSize = BufSize == 0 ? 0x100 : BufSize * 2;
+ dio_gFormBlockBuf = (U1_T *)loc_realloc(dio_gFormBlockBuf, BufSize);
+ }
+}
+
+char * dio_ReadString(void) {
+ char * Res = NULL;
+ U4_T Length = 0;
+ for (;;) {
+ U1_T Char = dio_ReadU1();
+ dio_CheckBlockBufCapacity(Length + 1);
+ dio_gFormBlockBuf[Length++] = Char;
+ if (Char == 0) break;
+ }
+ if (Length == 1) return NULL;
+ Res = (char *)loc_alloc(Length);
+ strcpy(Res, (char *)dio_gFormBlockBuf);
+ return Res;
+}
+
+U8_T dio_ReadAddrBuf(U1_T * Buf) {
+ int i;
+ U8_T Addr = 0;
+ for (i = 0; i < dio_gAddressSize; i++) {
+ U8_T Byte = (U8_T)Buf[i];
+ if (sBigEndian) {
+ Addr |= Byte << ((dio_gAddressSize - i - 1) * 8);
+ }
+ else {
+ Addr |= Byte << (i * 8);
+ }
+ }
+ return Addr;
+}
+
+static U1_T * dio_LoadStringTable(U4_T * StringTableSize) {
+ ELF_File * File = sSection->file;
+ DIO_Cache * Cache = dio_GetCache(File);
+
+ if (Cache->mStringTable == NULL) {
+ U4_T ID;
+ ELF_Section * Section = NULL;
+
+ for (ID = 1; ID < File->section_cnt; ID++) {
+ if (strcmp(File->sections[ID]->name, ".debug_str") == 0) {
+ if (Section != NULL) {
+ str_exception(ERR_DWARF, "more then one .debug_str section in a file");
+ }
+ Section = File->sections[ID];
+ assert(Section->file == File);
+ }
+ }
+
+ if (Section == NULL) {
+ str_exception(ERR_DWARF, "section .debug_str not found");
+ }
+
+ Cache->mStringTableSize = (size_t)Section->size;
+ if (elf_load(Section, &Cache->mStringTable) < 0) {
+ str_exception(ERR_DWARF, "invalid .debug_str section");
+ }
+ }
+
+ *StringTableSize = Cache->mStringTableSize;
+ return Cache->mStringTable;
+}
+
+static void dio_ReadFormAddr(void) {
+ dio_gFormRef = dio_ReadAddress();
+}
+
+static void dio_ReadFormBlock(U2_T Attr, U4_T Size) {
+ U1_T * Buf;
+ dio_gFormBlockSize = Size;
+ dio_CheckBlockBufCapacity(Size);
+ Buf = dio_gFormBlockBuf;
+ while (Size > 0) {
+ *Buf++ = dio_ReadU1();
+ Size--;
+ }
+}
+
+static void dio_ReadFormData(U2_T Attr, U1_T Size, U8_T Data) {
+ dio_gFormData = Data;
+ dio_gFormDataSize = Size;
+}
+
+static void dio_ReadFormFlag(void) {
+ dio_gFormData = dio_ReadU1();
+ dio_gFormDataSize = 1;
+}
+
+static void dio_ReadFormRef(void) {
+ dio_gFormRef = dio_ReadU4();
+}
+
+static void dio_ReadFormRelRef(U8_T Offset) {
+ if (dio_gUnitSize > 0 && Offset >= dio_gUnitSize) {
+ str_exception(ERR_DWARF, "invalid REF attribute value");
+ }
+ dio_gFormRef = dio_gUnitPos + Offset;
+}
+
+static void dio_ReadFormRefAddr(void) {
+ U4_T Size = dio_gAddressSize;
+ if (dio_gVersion >= 3) Size = dio_g64bit ? 8 : 4;
+ dio_gFormRef = dio_ReadUX(Size);
+}
+
+static void dio_ReadFormString(void) {
+ dio_gFormBlockSize = 0;
+ for (;;) {
+ U1_T Char = dio_ReadU1();
+ dio_CheckBlockBufCapacity(dio_gFormBlockSize + 1);
+ dio_gFormBlockBuf[dio_gFormBlockSize++] = Char;
+ if (Char == 0) break;
+ }
+}
+
+static void dio_ReadFormStringRef(void) {
+ U8_T Offset = dio_ReadUX(dio_g64bit ? 8 : 4);
+ U4_T StringTableSize = 0;
+ U1_T * StringTable = dio_LoadStringTable(&StringTableSize);
+ dio_gFormBlockSize = 0;
+ for (;;) {
+ U1_T Char;
+ if (Offset >= StringTableSize) {
+ str_exception(ERR_DWARF, "invalid FORM_STRP attribute");
+ }
+ dio_CheckBlockBufCapacity(dio_gFormBlockSize + 1);
+ Char = StringTable[Offset++];
+ dio_gFormBlockBuf[dio_gFormBlockSize++] = Char;
+ if (Char == 0) break;
+ }
+}
+
+static void dio_ReadAttribute(U2_T Attr, U2_T Form) {
+ switch (Form) {
+ case FORM_ADDR : dio_ReadFormAddr(); break;
+ case FORM_REF : dio_ReadFormRef(); break;
+ case FORM_BLOCK1 : dio_ReadFormBlock(Attr, dio_ReadU1()); break;
+ case FORM_BLOCK2 : dio_ReadFormBlock(Attr, dio_ReadU2()); break;
+ case FORM_BLOCK4 : dio_ReadFormBlock(Attr, dio_ReadU4()); break;
+ case FORM_BLOCK : dio_ReadFormBlock(Attr, dio_ReadLEB128()); break;
+ case FORM_DATA1 : dio_ReadFormData(Attr, 1, dio_ReadU1()); break;
+ case FORM_DATA2 : dio_ReadFormData(Attr, 2, dio_ReadU2()); break;
+ case FORM_DATA4 : dio_ReadFormData(Attr, 4, dio_ReadU4()); break;
+ case FORM_DATA8 : dio_ReadFormData(Attr, 8, dio_ReadU8()); break;
+ case FORM_SDATA : dio_ReadFormData(Attr, 8, dio_ReadI8LEB128()); break;
+ case FORM_UDATA : dio_ReadFormData(Attr, 8, dio_ReadU8LEB128()); break;
+ case FORM_FLAG : dio_ReadFormFlag(); break;
+ case FORM_STRING : dio_ReadFormString(); break;
+ case FORM_STRP : dio_ReadFormStringRef(); break;
+ case FORM_REF_ADDR : dio_ReadFormRefAddr(); break;
+ case FORM_REF1 : dio_ReadFormRelRef(dio_ReadU1()); break;
+ case FORM_REF2 : dio_ReadFormRelRef(dio_ReadU2()); break;
+ case FORM_REF4 : dio_ReadFormRelRef(dio_ReadU4()); break;
+ case FORM_REF8 : dio_ReadFormRelRef(dio_ReadU8()); break;
+ case FORM_REF_UDATA : dio_ReadFormRelRef(dio_ReadLEB128()); break;
+ default: str_exception(ERR_DWARF, "invalid FORM");
+ }
+}
+
+static void dio_ReadEntry(DIO_EntryCallBack CallBack) {
+ DIO_Abbreviation * Abbr = NULL;
+ U2_T Tag = 0;
+ U4_T AttrPos = 0;
+ U4_T EntrySize = 0;
+ int Init = 1;
+ dio_gEntryPos = dio_GetPos();
+ if (dio_gVersion >= 2) {
+ U4_T AbbrCode = dio_ReadLEB128();
+ if (AbbrCode == 0) return;
+ if (AbbrCode >= sAbbrevTableSize || sAbbrevTable[AbbrCode] == NULL) {
+ str_exception(ERR_DWARF, "invalid abbreviation table");
+ }
+ Abbr = sAbbrevTable[AbbrCode];
+ Tag = Abbr->mTag;
+ }
+ else {
+ EntrySize = dio_ReadU4();
+ if (EntrySize < 8) {
+ while (EntrySize > 4) {
+ dio_ReadU1();
+ EntrySize--;
+ }
+ return;
+ }
+ Tag = dio_ReadU2();
+ }
+ for (;;) {
+ U2_T Attr = 0;
+ U2_T Form = 0;
+ if (Init) {
+ Form = 1;
+ Init = 0;
+ }
+ else if (Abbr != NULL) {
+ if (AttrPos < Abbr->mAttrLen) {
+ Attr = Abbr->mAttrs[AttrPos++];
+ Form = Abbr->mAttrs[AttrPos++];
+ if (Form == FORM_INDIRECT) Form = (U2_T)dio_ReadLEB128();
+ }
+ }
+ else {
+ if (dio_GetPos() < dio_gEntryPos + EntrySize) {
+ Attr = dio_ReadU2();
+ Form = Attr & 0xF;
+ Attr = (Attr & 0xfff0) >> 4;
+ }
+ }
+ if (Attr != 0 && Form != 0) dio_ReadAttribute(Attr, Form);
+ if (Tag == TAG_compile_unit) {
+ if (Attr == AT_sibling && dio_gUnitSize == 0) {
+ dio_ChkRef(Form);
+ assert(dio_gVersion == 1);
+ dio_gUnitSize = (U4_T)(dio_gFormRef - dio_gUnitPos);
+ assert(dio_gUnitPos < dio_GetPos());
+ assert(dio_gUnitPos + dio_gUnitSize >= dio_GetPos());
+ }
+ else if (Attr == 0 && Form == 0) {
+ if (dio_gUnitSize == 0) str_exception(ERR_DWARF, "missing compilation unit sibling attribute");
+ }
+ }
+ CallBack(Tag, Attr, Form);
+ if (Attr == 0 && Form == 0) break;
+ }
+}
+
+static void dio_FindAbbrevTable(U4_T Offset, DIO_Abbreviation *** AbbrevTable, U4_T * AbbrevTableSize);
+
+void dio_ReadUnit(DIO_EntryCallBack CallBack) {
+ dio_gUnitPos = dio_GetPos();
+ dio_g64bit = 0;
+ if (dio_gVersion >= 2) {
+ dio_gUnitSize = dio_ReadU4();
+ if (dio_gUnitSize == 0xffffffffu) {
+ dio_g64bit = 1;
+ str_exception(ERR_DWARF, "64-bit DWARF is not supported yet");
+ }
+ else {
+ dio_gUnitSize += 4;
+ }
+ dio_gVersion = dio_ReadU2();
+ dio_FindAbbrevTable(dio_ReadU4(), &sAbbrevTable, &sAbbrevTableSize);
+ dio_gAddressSize = dio_ReadU1();
+ }
+ else {
+ dio_gUnitSize = 0;
+ dio_gVersion = 1;
+ dio_gAddressSize = 4;
+ sAbbrevTable = NULL;
+ sAbbrevTableSize = 0;
+ }
+ while (dio_gUnitSize == 0 || dio_GetPos() < dio_gUnitPos + dio_gUnitSize) {
+ dio_ReadEntry(CallBack);
+ }
+}
+
+#define dio_AbbrevTableHash(File, Offset) (((int)(File) + (int)(Offset)) / 4 % ABBREV_TABLE_SIZE)
+
+static int dio_IsAbbrevSectionLoaded(ELF_File * File) {
+ DIO_Cache * Cache = dio_GetCache(File);
+
+ if (Cache->mAbbrevTable != NULL) {
+ U4_T Hash = dio_AbbrevTableHash(File, 0);
+ DIO_AbbrevSet * AbbrevSet = Cache->mAbbrevTable[Hash];
+ while (AbbrevSet != NULL) {
+ if (AbbrevSet->mFile == File) return 1;
+ AbbrevSet = AbbrevSet->mNext;
+ }
+ }
+ return 0;
+}
+
+void dio_LoadAbbrevTable(ELF_File * File) {
+ U4_T ID;
+ U8_T TableOffset = 0;
+ ELF_Section * Section = NULL;
+ static U2_T * AttrBuf = NULL;
+ static U4_T AttrBufSize = 0;
+ DIO_Abbreviation ** AbbrevTable = NULL;
+ U4_T AbbrevTableSize = 0;
+ DIO_Cache * Cache = dio_GetCache(File);
+
+ if (dio_IsAbbrevSectionLoaded(File)) return;
+
+ assert(sSection == NULL);
+ for (ID = 1; ID < File->section_cnt; ID++) {
+ if (strcmp(File->sections[ID]->name, ".debug_abbrev") == 0) {
+ if (Section != NULL) {
+ str_exception(ERR_DWARF, "more then one .debug_abbrev section in a file");
+ }
+ Section = File->sections[ID];
+ }
+ }
+ if (Section == NULL) return;
+ dio_EnterSection(Section, 0);
+ for (;;) {
+ U4_T AttrPos = 0;
+ U2_T Tag = 0;
+ U1_T Children = 0;
+ U4_T ID = dio_ReadLEB128();
+ if (ID == 0) {
+ /* End of compilation unit */
+ U4_T Hash = dio_AbbrevTableHash(File, TableOffset);
+ DIO_AbbrevSet * AbbrevSet = (DIO_AbbrevSet *)loc_alloc_zero(sizeof(DIO_AbbrevSet));
+ AbbrevSet->mFile = File;
+ AbbrevSet->mOffset = TableOffset;
+ AbbrevSet->mTable = AbbrevTable;
+ AbbrevSet->mSize = AbbrevTableSize;
+ if (Cache->mAbbrevTable == NULL) {
+ Cache->mAbbrevTable = (DIO_AbbrevSet **)loc_alloc_zero(sizeof(DIO_AbbrevSet *) * ABBREV_TABLE_SIZE);
+ }
+ AbbrevSet->mNext = Cache->mAbbrevTable[Hash];
+ Cache->mAbbrevTable[Hash] = AbbrevSet;
+ AbbrevTable = NULL;
+ AbbrevTableSize = 0;
+ if (dio_GetPos() >= Section->size) break;
+ TableOffset = dio_GetPos();
+ continue;
+ }
+ if (ID >= 0x1000000) str_exception(ERR_DWARF, "invalid abbreviation table");
+ if (ID >= AbbrevTableSize) {
+ U4_T Size = AbbrevTableSize;
+ AbbrevTableSize = ID + 1024u;
+ AbbrevTable = (DIO_Abbreviation **)loc_realloc(AbbrevTable, sizeof(DIO_Abbreviation *) * AbbrevTableSize);
+ memset(AbbrevTable + Size, 0, sizeof(DIO_Abbreviation *) * (AbbrevTableSize - Size));
+ }
+ Tag = (U2_T)dio_ReadLEB128();
+ Children = (U2_T)dio_ReadU1() != 0;
+ for (;;) {
+ U4_T Attr = dio_ReadLEB128();
+ U4_T Form = dio_ReadLEB128();
+ if (Attr >= 0x10000 || Form >= 0x10000) str_exception(ERR_DWARF, "invalid abbreviation table");
+ if (Attr == 0 && Form == 0) {
+ DIO_Abbreviation * Abbr = AbbrevTable[ID];
+ assert(Abbr == NULL);
+ Abbr = (DIO_Abbreviation *)loc_alloc_zero(sizeof(DIO_Abbreviation) + sizeof(U2_T) * (AttrPos - 2));
+ Abbr->mTag = Tag;
+ Abbr->mChildren = Children;
+ Abbr->mAttrLen = AttrPos;
+ memcpy(Abbr->mAttrs, AttrBuf, sizeof(U2_T) * AttrPos);
+ AbbrevTable[ID] = Abbr;
+ break;
+ }
+ if (AttrBufSize < AttrPos + 2) {
+ AttrBufSize = AttrPos + 256;
+ AttrBuf = (U2_T *)loc_realloc(AttrBuf, sizeof(U2_T) * AttrBufSize);
+ }
+ AttrBuf[AttrPos++] = (U2_T)Attr;
+ AttrBuf[AttrPos++] = (U2_T)Form;
+ }
+ }
+ assert(AbbrevTable == NULL);
+ assert(AbbrevTableSize == 0);
+ dio_ExitSection();
+}
+
+static void dio_FindAbbrevTable(U4_T Offset, DIO_Abbreviation *** AbbrevTable, U4_T * AbbrevTableSize) {
+ DIO_Cache * Cache = dio_GetCache(sSection->file);
+ if (Cache->mAbbrevTable != NULL) {
+ U4_T Hash = dio_AbbrevTableHash(sSection->file, Offset);
+ DIO_AbbrevSet * AbbrevSet = Cache->mAbbrevTable[Hash];
+ while (AbbrevSet != NULL) {
+ if (AbbrevSet->mFile == sSection->file && AbbrevSet->mOffset == Offset) {
+ *AbbrevTable = AbbrevSet->mTable;
+ *AbbrevTableSize = AbbrevSet->mSize;
+ return;
+ }
+ AbbrevSet = AbbrevSet->mNext;
+ }
+ }
+ str_exception(ERR_DWARF, "invalid abbreviation table offset");
+ *AbbrevTable = NULL;
+ *AbbrevTableSize = 0;
+}
+
+void dio_ChkFlag(U2_T Form) {
+ switch (Form) {
+ case FORM_FLAG :
+ return;
+ }
+ str_exception(ERR_DWARF, "FORM_FLAG expected");
+}
+
+void dio_ChkRef(U2_T Form) {
+ switch (Form) {
+ case FORM_REF :
+ case FORM_REF_ADDR :
+ case FORM_REF1 :
+ case FORM_REF2 :
+ case FORM_REF4 :
+ case FORM_REF8 :
+ case FORM_REF_UDATA :
+ return;
+ }
+ str_exception(ERR_DWARF, "FORM_REF* expected");
+}
+
+void dio_ChkAddr(U2_T Form) {
+ switch (Form) {
+ case FORM_ADDR :
+ return;
+ }
+ str_exception(ERR_DWARF, "FORM_ADDR expected");
+}
+
+void dio_ChkData(U2_T Form) {
+ switch (Form) {
+ case FORM_DATA1 :
+ case FORM_DATA2 :
+ case FORM_DATA4 :
+ case FORM_DATA8 :
+ case FORM_SDATA :
+ case FORM_UDATA :
+ return;
+ }
+ str_exception(ERR_DWARF, "FORM_DATA* expected");
+}
+
+void dio_ChkBlock(U2_T Form, U1_T ** Buf, U4_T * Size) {
+ switch (Form) {
+ case FORM_BLOCK1 :
+ case FORM_BLOCK2 :
+ case FORM_BLOCK4 :
+ case FORM_BLOCK :
+ *Size = dio_gFormBlockSize;
+ *Buf = dio_gFormBlockBuf;
+ break;
+ case FORM_DATA1 :
+ case FORM_DATA2 :
+ case FORM_DATA4 :
+ case FORM_DATA8 :
+ case FORM_SDATA :
+ case FORM_UDATA :
+ *Size = dio_gFormDataSize;
+ *Buf = (U1_T *)&dio_gFormData;
+ break;
+ default:
+ str_exception(ERR_DWARF, "FORM_BLOCK expected");
+ }
+}
+
+void dio_ChkString(U2_T Form) {
+ if (Form == FORM_STRING) return;
+ if (Form == FORM_STRP) return;
+ str_exception(ERR_DWARF, "FORM_STRING expected");
+}
+
+#endif
diff --git a/dwarfio.h b/dwarfio.h
new file mode 100644
index 00000000..395c08fb
--- /dev/null
+++ b/dwarfio.h
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2006-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
+ *******************************************************************************/
+
+/*
+ * This module implements low-level functions for reading DWARF debug information.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+#ifndef D_dwarfio
+#define D_dwarfio
+
+#include "elf.h"
+
+extern U4_T dio_gVersion;
+extern U1_T dio_g64bit;
+extern U1_T dio_gAddressSize;
+extern U8_T dio_gUnitPos;
+extern U4_T dio_gUnitSize;
+extern U8_T dio_gEntryPos;
+
+extern U8_T dio_gFormRef;
+extern U8_T dio_gFormData;
+extern U1_T dio_gFormDataSize;
+extern U1_T * dio_gFormBlockBuf;
+extern U4_T dio_gFormBlockSize;
+
+extern void dio_EnterSection(ELF_Section * Section, U8_T Offset);
+extern void dio_ExitSection(void);
+
+extern void dio_Skip(U8_T Bytes);
+extern void dio_Read(U1_T * Buf, U4_T Size);
+extern U8_T dio_GetPos(void);
+
+extern U1_T dio_ReadU1(void);
+extern U2_T dio_ReadU2(void);
+extern U4_T dio_ReadU4(void);
+extern U8_T dio_ReadU8(void);
+
+extern U4_T dio_ReadLEB128(void);
+extern U8_T dio_ReadU8LEB128(void);
+extern I8_T dio_ReadI8LEB128(void);
+
+extern U8_T dio_ReadUX(int Size);
+extern U8_T dio_ReadAddress(void);
+
+extern char * dio_ReadString(void);
+
+extern U8_T dio_ReadAddrBuf(U1_T * Buf);
+
+typedef void (*DIO_EntryCallBack)(U2_T /* Tag */, U2_T /* Attr */, U2_T /* Form */);
+/*
+ * CallBack is called berore each DWARF entry with Atrr = 0 and Form = 1,
+ * then is is called for each entry attribute with appropriate Attr and Form values,
+ * and then called after the entry with Attr = 0 and Form = 0.
+ * This sequence is repeated for each entry in the debug info unit.
+ */
+extern void dio_ReadUnit(DIO_EntryCallBack CallBack);
+
+extern void dio_LoadAbbrevTable(ELF_File * File);
+
+extern void dio_ChkFlag(U2_T Form);
+extern void dio_ChkRef(U2_T Form);
+extern void dio_ChkAddr(U2_T Form);
+extern void dio_ChkData(U2_T Form);
+extern void dio_ChkBlock(U2_T Form, U1_T ** Buf, U4_T * Size);
+extern void dio_ChkString(U2_T Form);
+
+#endif
+
diff --git a/elf.c b/elf.c
new file mode 100644
index 00000000..8476ca84
--- /dev/null
+++ b/elf.c
@@ -0,0 +1,235 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements reading and caching of ELF files.
+ */
+#include "config.h"
+#if SERVICE_LineNumbers || SERVICE_Symbols
+
+#include <assert.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "elf.h"
+#include "myalloc.h"
+
+#if defined(_WRS_KERNEL)
+#elif defined(WIN32)
+#else
+# include <libelf.h>
+# define USE_LIBELF
+#endif
+
+#define MAX_CACHED_FILES 8
+
+static ELF_File * files = NULL;
+static ELFCloseListener * listeners = NULL;
+static U4_T listeners_cnt = 0;
+static U4_T listeners_max = 0;
+
+static void elf_dispose(ELF_File * file) {
+ U4_T n;
+ assert(file->ref_cnt == 0);
+ for (n = 0; n < listeners_cnt; n++) {
+ listeners[n](file);
+ }
+#ifdef USE_LIBELF
+ if (file->libelf_cache != NULL) elf_end(file->libelf_cache);
+#endif
+ if (file->fd >= 0) close(file->fd);
+ if (file->sections != NULL) {
+ for (n = 0; n < file->section_cnt; n++) {
+ loc_free(file->sections[n]);
+ }
+ loc_free(file->sections);
+ }
+ free(file->name);
+ loc_free(file);
+}
+
+ELF_File * elf_open(char * file_name) {
+ int cnt = 0;
+ int error = 0;
+ struct_stat st;
+ ELF_File * prev = NULL;
+ ELF_File * file = files;
+
+ file_name = canonicalize_file_name(file_name);
+ if (file_name == NULL) return NULL;
+ if (stat(file_name, &st) < 0) {
+ error = errno;
+ free(file_name);
+ errno = error;
+ return NULL;
+ }
+ while (file != NULL) {
+ if (strcmp(file->name, file_name) == 0 &&
+ file->dev == st.st_dev &&
+ file->ino == st.st_ino &&
+ file->mtime == st.st_mtime) {
+ if (prev != NULL) {
+ prev->next = file->next;
+ file->next = files;
+ files = file;
+ }
+ file->ref_cnt++;
+ free(file_name);
+ return file;
+ }
+ if (cnt >= MAX_CACHED_FILES && file->ref_cnt == 0) {
+ prev->next = file->next;
+ elf_dispose(file);
+ file = prev->next;
+ }
+ else {
+ prev = file;
+ file = file->next;
+ cnt++;
+ }
+ }
+
+ file = (ELF_File *)loc_alloc_zero(sizeof(ELF_File));
+ file->name = file_name;
+ file->dev = st.st_dev;
+ file->ino = st.st_ino;
+ file->mtime = st.st_mtime;
+#ifdef _WRS_KERNEL
+ if ((file->fd = open(file->name, O_RDONLY, 0)) < 0) {
+#else
+ if ((file->fd = open(file->name, O_RDONLY)) < 0) {
+#endif
+ error = errno;
+ }
+
+#ifdef USE_LIBELF
+ if (error == 0) {
+ Elf * elf;
+ Elf32_Ehdr * ehdr;
+ /* Obtain the ELF descriptor */
+ (void)elf_version(EV_CURRENT);
+ if ((elf = elf_begin(file->fd, ELF_C_READ, NULL)) == NULL) {
+ error = errno;
+ }
+ else {
+ file->libelf_cache = elf;
+ if ((ehdr = elf32_getehdr(elf)) == NULL) {
+ error = errno;
+ }
+ }
+ if (error == 0) {
+ size_t snum = 0;
+ size_t shstrndx = 0;
+ if (elf_getshnum(elf, &snum) < 0) error = errno;
+ if (error == 0) {
+ file->sections = (ELF_Section **)loc_alloc_zero(sizeof(ELF_Section *) * snum);
+ file->section_cnt = snum;
+ if (elf_getshstrndx(elf, &shstrndx) < 0) error = errno;
+ }
+ if (error == 0) {
+ /* Iterate sections */
+ Elf_Scn * scn = NULL;
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ Elf32_Shdr * shdr = NULL;
+ char * name = NULL;
+ ELF_Section * sec = NULL;
+ if ((shdr = elf32_getshdr(scn)) == NULL) {
+ error = errno;
+ break;
+ }
+ if ((name = elf_strptr(elf, shstrndx, shdr->sh_name)) == NULL) {
+ error = errno;
+ break;
+ }
+ sec = (ELF_Section *)loc_alloc(sizeof(ELF_Section));
+ sec->file = file;
+ sec->index = elf_ndxscn(scn);
+ sec->name = name;
+ sec->type = shdr->sh_type;
+ sec->offset = shdr->sh_offset;
+ sec->size = shdr->sh_size;
+ sec->flags = shdr->sh_flags;
+ sec->addr = shdr->sh_addr;
+ sec->link = shdr->sh_link;
+ sec->info = shdr->sh_info;
+ assert(sec->index < snum);
+ file->sections[sec->index] = sec;
+ }
+ }
+ }
+ }
+#else
+ if (error == 0) {
+ error = EINVAL;
+ }
+#endif
+ if (error != 0) {
+ elf_dispose(file);
+ errno = error;
+ return NULL;
+ }
+ file->ref_cnt = 1;
+ file->next = files;
+ return files = file;
+}
+
+int elf_load(ELF_Section * section, U1_T ** address) {
+#ifdef USE_LIBELF
+ Elf * elf = (Elf *)section->file->libelf_cache;
+ Elf_Scn * scn = elf_getscn(elf, section->index);
+ Elf_Data * data = elf_getdata(scn, NULL);
+ if (data == NULL) return -1;
+ assert(data->d_buf != NULL && data->d_size == section->size);
+ *address = data->d_buf;
+ return 0;
+#else
+ *address = NULL;
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
+int elf_read(ELF_Section * section, U8_T offset, U1_T * buf, U4_T size, U4_T * rd_len) {
+#ifdef USE_LIBELF
+ U4_T rd = size;
+ Elf * elf = (Elf *)section->file->libelf_cache;
+ Elf_Scn * scn = elf_getscn(elf, section->index);
+ Elf_Data * data = elf_getdata(scn, NULL);
+ if (data == NULL) return -1;
+ assert(data->d_buf != NULL && data->d_size == section->size);
+ assert(offset < section->size);
+ if (offset + rd > section->size) rd = (U4_T)(section->size - offset);
+ memcpy(buf, data->d_buf + offset, rd);
+ *rd_len = rd;
+ return 0;
+#else
+ *rd_len = 0;
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
+void elf_close(ELF_File * file) {
+ assert(file != NULL);
+ assert(file->ref_cnt > 0);
+ file->ref_cnt--;
+}
+
+void elf_add_close_listener(ELFCloseListener listener) {
+ if (listeners_cnt >= listeners_max) {
+ listeners_max = listeners_max == 0 ? 16 : listeners_max * 2;
+ listeners = (ELFCloseListener *)loc_realloc(listeners, sizeof(ELFCloseListener) * listeners_max);
+ }
+ listeners[listeners_cnt++] = listener;
+}
+
+#endif
diff --git a/elf.h b/elf.h
new file mode 100644
index 00000000..dc39a08f
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements reading and caching of ELF files.
+ */
+#ifndef D_elf
+#define D_elf
+
+#include <sys/types.h>
+#if defined(WIN32)
+#else
+# include <elf.h>
+#endif
+#include "mdep.h"
+
+typedef unsigned char U1_T;
+typedef signed char I1_T;
+typedef unsigned short U2_T;
+typedef signed short I2_T;
+typedef unsigned int U4_T;
+typedef signed int I4_T;
+typedef uns64 U8_T;
+typedef int64 I8_T;
+
+struct ELF_File;
+struct ELF_Section;
+typedef struct ELF_Section ELF_Section;
+typedef struct ELF_File ELF_File;
+
+struct ELF_File {
+ ELF_File * next;
+ U4_T ref_cnt;
+
+ char * name;
+ dev_t dev;
+ ino_t ino;
+ time_t mtime;
+ int fd;
+
+ int big_endian;
+ U4_T section_cnt;
+ ELF_Section ** sections;
+
+ void * libelf_cache;
+ void * dwarf_cache;
+ void * sym_cache;
+ void * line_numbers_cache;
+};
+
+struct ELF_Section {
+ ELF_File * file;
+ U4_T index;
+ char * name;
+ U4_T type;
+ U4_T flags;
+ U8_T offset;
+ U8_T size;
+ U8_T addr;
+ U4_T link;
+ U4_T info;
+};
+
+/*
+ * Open ELF file for reading.
+ * Same file can be opened mutiple times, each call to elf_open() increases reference counter.
+ * File must be closed after usage by calling elf_close().
+ * Returns the file descriptior on success. If error, returns NULL and sets errno.
+ */
+extern ELF_File * elf_open(char * file_name);
+
+/*
+ * Read section data from ELF file.
+ * '*rd_len' is set to number of bytes transfered into the buffer.
+ * Returns zero on success. If error, returns -1 and sets errno.
+ */
+extern int elf_read(ELF_Section * section, U8_T offset, U1_T * buf, U4_T size, U4_T * rd_len);
+
+/*
+ * Load section data into memory.
+ * '*address' is set to section data address in memory.
+ * Data will stay in memory at least until file is closed.
+ * Returns zero on success. If error, returns -1 and sets errno.
+ */
+extern int elf_load(ELF_Section * section, U1_T ** address);
+
+/*
+ * Close ELF file.
+ * Each call of elf_close() decrements reference counter.
+ * The file be kept in cache for some time even after all references are closed.
+ */
+extern void elf_close(ELF_File * file);
+
+/*
+ * Register ELF file close callback.
+ * The callback is called each time an ELF file data is about to be disposed.
+ * Service implementation can use the callback to deallocate
+ * cached data related to the file.
+ */
+typedef void (*ELFCloseListener)(ELF_File *);
+extern void elf_add_close_listener(ELFCloseListener listener);
+
+#endif
+
diff --git a/errors.c b/errors.c
new file mode 100644
index 00000000..691f9bec
--- /dev/null
+++ b/errors.c
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module defines agent error codes in addition to system codes defined in errno.h
+ */
+
+#include <string.h>
+#include "errors.h"
+
+char * errno_to_str(int err) {
+ switch (err) {
+ case ERR_ALREADY_STOPPED:
+ return "Already stopped";
+ case ERR_ALREADY_EXITED:
+ return "Already exited";
+ case ERR_ALREADY_RUNNING:
+ return "Already running";
+ case ERR_JSON_SYNTAX:
+ return "JSON syntax error";
+ case ERR_PROTOCOL:
+ return "Protocol format error";
+ case ERR_INV_CONTEXT:
+ return "Invalid context ID";
+ case ERR_INV_ADDRESS:
+ return "Invalid address";
+ case ERR_EOF:
+ return "End of file";
+ case ERR_BASE64:
+ return "Invalid BASE64 string";
+ case ERR_INV_EXPRESSION:
+ return "Invalid expression";
+ case ERR_SYM_NOT_FOUND:
+ return "Symbol not found";
+ case ERR_ALREADY_ATTACHED:
+ return "Already attached";
+ case ERR_BUFFER_OVERFLOW:
+ return "Buffer overflow";
+ case ERR_INV_FORMAT:
+ return "Format is not supported";
+ case ERR_INV_NUMBER:
+ return "Invalid number";
+ case ERR_IS_RUNNING:
+ return "Execution context is running";
+ case ERR_DWARF:
+ return "Error reading DWARF data";
+ default:
+ return strerror(err);
+ }
+}
diff --git a/errors.h b/errors.h
new file mode 100644
index 00000000..f7e8f679
--- /dev/null
+++ b/errors.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * This module defines agent error codes in addition to system codes defined in errno.h
+ */
+
+#ifndef D_errors
+#define D_errors
+
+#include <errno.h>
+
+#define ERR_ALREADY_STOPPED 0x1000
+#define ERR_ALREADY_EXITED 0x1001
+#define ERR_ALREADY_RUNNING 0x1002
+#define ERR_JSON_SYNTAX 0x1003
+#define ERR_PROTOCOL 0x1004
+#define ERR_INV_CONTEXT 0x1005
+#define ERR_INV_ADDRESS 0x1006
+#define ERR_EOF 0x1007
+#define ERR_BASE64 0x1008
+#define ERR_INV_EXPRESSION 0x1009
+#define ERR_SYM_NOT_FOUND 0x100a
+#define ERR_ALREADY_ATTACHED 0x100b
+#define ERR_BUFFER_OVERFLOW 0x100c
+#define ERR_INV_FORMAT 0x100d
+#define ERR_INV_NUMBER 0x100e
+#define ERR_IS_RUNNING 0x100f
+#define ERR_DWARF 0x1010
+
+extern char * errno_to_str(int err);
+
+#endif
diff --git a/events.c b/events.c
new file mode 100644
index 00000000..d29f1020
--- /dev/null
+++ b/events.c
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Event queue manager.
+ * Event is a data pointer plus a function pointer (a.k.a. event handler).
+ *
+ * Posting event means placing event into event queue.
+ * Dispatching event means removing event from the queue and then calling
+ * event function with event data as argument.
+ *
+ * All events are dispatched by single thread - dispatch thread. This makes it safe
+ * to access global data structures from event handlers without further synchronization,
+ * while allows for high level of concurrency.
+ */
+
+#include <time.h>
+#include <assert.h>
+#include "mdep.h"
+#include "myalloc.h"
+#include "trace.h"
+#include "events.h"
+
+typedef struct event_node event_node;
+
+struct event_node {
+ event_node * next;
+ struct timespec runtime;
+ void (*handler)(void *);
+ void *arg;
+};
+
+pthread_t event_thread = 0;
+
+static pthread_mutex_t event_lock;
+static pthread_cond_t event_cond;
+
+static event_node * event_queue = NULL;
+static event_node * event_last = NULL;
+static event_node * timer_queue = NULL;
+static event_node * free_queue = NULL;
+static int free_queue_size = 0;
+
+static int time_cmp(const struct timespec *tv1, const struct timespec *tv2) {
+ if (tv1->tv_sec < tv2->tv_sec) return -1;
+ if (tv1->tv_sec > tv2->tv_sec) return 1;
+ if (tv1->tv_nsec < tv2->tv_nsec) return -1;
+ if (tv1->tv_nsec > tv2->tv_nsec) return 1;
+ return 0;
+}
+
+/*
+ * Add microsecond value to timeval.
+ */
+static void time_add_usec(struct timespec *tv, unsigned long usec) {
+ tv->tv_sec += usec / 1000000;
+ tv->tv_nsec += (usec % 1000000) * 1000;
+ if (tv->tv_nsec >= 1000000000) {
+ tv->tv_sec++;
+ tv->tv_nsec -= 1000000000;
+ }
+}
+
+static event_node * alloc_node(void (*handler)(void *), void *arg) {
+ event_node * node;
+ if (free_queue != NULL) {
+ node = free_queue;
+ free_queue = node->next;
+ free_queue_size--;
+ }
+ else {
+ node = (event_node *)loc_alloc(sizeof(event_node));
+ }
+ memset(node, 0, sizeof(event_node));
+ node->handler = handler;
+ node->arg = arg;
+ return node;
+}
+
+static void free_node(event_node * node) {
+ if (free_queue_size < 500) {
+ node->next = free_queue;
+ free_queue = node;
+ free_queue_size++;
+ }
+ else {
+ loc_free(node);
+ }
+}
+
+void post_event_with_delay(void (*handler)(void *), void *arg, unsigned long delay) {
+ event_node * ev;
+ event_node * qp;
+ event_node ** qpp;
+
+ pthread_mutex_lock(&event_lock);
+ ev = alloc_node(handler, arg);
+ if (clock_gettime(CLOCK_REALTIME, &ev->runtime)) {
+ perror("clock_gettime");
+ exit(1);
+ }
+ time_add_usec(&ev->runtime, delay);
+
+ qpp = &timer_queue;
+ while ((qp = *qpp) != 0 && time_cmp(&ev->runtime, &qp->runtime) >= 0) {
+ qpp = &qp->next;
+ }
+ ev->next = qp;
+ *qpp = ev;
+ if (timer_queue == ev) {
+ pthread_cond_signal(&event_cond);
+ }
+ trace(LOG_EVENTCORE, "post_event: event %#x handler %#x arg %#x runtime %02d%02d.%03d",
+ ev, ev->handler, ev->arg, ev->runtime.tv_sec/60%60, ev->runtime.tv_sec%60, ev->runtime.tv_nsec/1000000);
+ pthread_mutex_unlock(&event_lock);
+}
+
+void post_event(void (*handler)(void *), void *arg) {
+ event_node * ev;
+
+ pthread_mutex_lock(&event_lock);
+ ev = alloc_node(handler, arg);
+
+ if (event_queue == NULL) {
+ assert(event_last == NULL);
+ event_last = event_queue = ev;
+ pthread_cond_signal(&event_cond);
+ }
+ else {
+ event_last->next = ev;
+ event_last = ev;
+ }
+ trace(LOG_EVENTCORE, "post_event: event %#x handler %#x arg %#x", ev, ev->handler, ev->arg);
+ pthread_mutex_unlock(&event_lock);
+}
+
+int is_dispatch_thread(void) {
+ return event_thread == pthread_self();
+}
+
+void ini_events_queue(void) {
+ /* Initial thread is event dispatcher. */
+ event_thread = pthread_self();
+ pthread_mutex_init(&event_lock, NULL);
+ pthread_cond_init(&event_cond, NULL);
+}
+
+void run_event_loop(void) {
+ assert(is_dispatch_thread());
+ pthread_mutex_lock(&event_lock);
+ for (;;) {
+ event_node * ev = NULL;
+ if (event_queue != NULL) {
+ ev = event_queue;
+ event_queue = ev->next;
+ if (event_queue == NULL) {
+ assert(event_last == ev);
+ event_last = NULL;
+ }
+ }
+ else if (timer_queue != NULL) {
+ struct timespec timenow;
+ if (clock_gettime(CLOCK_REALTIME, &timenow)) {
+ perror("clock_gettime");
+ exit(1);
+ }
+ ev = timer_queue;
+ if (time_cmp(&timer_queue->runtime, &timenow) > 0) {
+ pthread_cond_timedwait(&event_cond, &event_lock, &ev->runtime);
+ continue;
+ }
+ timer_queue = ev->next;
+ }
+ else {
+ pthread_cond_wait(&event_cond, &event_lock);
+ continue;
+ }
+
+ pthread_mutex_unlock(&event_lock);
+ trace(LOG_EVENTCORE, "run_event_loop: event %#x handler %#x arg %#x", ev, ev->handler, ev->arg);
+ ev->handler(ev->arg);
+ pthread_mutex_lock(&event_lock);
+ free_node(ev);
+ }
+}
+
diff --git a/events.h b/events.h
new file mode 100644
index 00000000..d54ba01b
--- /dev/null
+++ b/events.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Event queue manager.
+ * Event is a data pointer plus a function pointer (a.k.a. event handler).
+ *
+ * Posting event means placing event into event queue.
+ * Dispatching event means removing event from the queue and then calling
+ * event function with event data as argument.
+ *
+ * All events are dispatched by single thread - dispatch thread. This makes it safe
+ * to access global data structures from event handlers without further synchronization,
+ * while allows for high level of concurrency.
+ */
+
+#ifndef D_events
+#define D_events
+
+extern void post_event(void (*handler)(void *), void *arg);
+extern void post_event_with_delay(void (*handler)(void *), void *arg, unsigned long us_delay);
+
+extern int is_dispatch_thread(void);
+
+extern void run_event_loop(void);
+
+extern void ini_events_queue(void);
+
+#endif
diff --git a/exceptions.c b/exceptions.c
new file mode 100644
index 00000000..f9910072
--- /dev/null
+++ b/exceptions.c
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Exception handling. Functionality is similar to C++ try/catch.
+ * Usage example:
+ Trap trap;
+ if (set_trap(&trap)) {
+ // Some code that can throw an exception by calling exception()
+ ...
+
+ clear_trap(&trap);
+ }
+ else {
+ // Exception handling code
+ if (trap.error == ...
+ ...
+ }
+ * Only main thread is allowed to use exceptions.
+ */
+
+#if _WRS_KERNEL
+# include <vxWorks.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "exceptions.h"
+#include "events.h"
+#include "trace.h"
+
+static Trap * chain = NULL;
+
+int set_trap_a(Trap * trap) {
+ assert(is_dispatch_thread());
+ memset(trap, 0, sizeof(Trap));
+ trap->next = chain;
+ chain = trap;
+ return 0;
+}
+
+int set_trap_b(Trap * trap) {
+ if (trap->error == 0) return 1;
+ assert(trap == chain);
+ chain = trap->next;
+ return 0;
+}
+
+void clear_trap(Trap * trap) {
+ assert(is_dispatch_thread());
+ assert(trap == chain);
+ chain = trap->next;
+}
+
+void exception(int error) {
+ assert(is_dispatch_thread());
+ assert(error != 0);
+ if (chain == NULL) {
+ trace(LOG_ALWAYS, "Unhandled exception %d: %s", error, errno_to_str(error));
+ exit(error);
+ }
+ longjmp(chain->env, error);
+}
+
+void str_exception(int error, char * msg) {
+ assert(is_dispatch_thread());
+ assert(error != 0);
+ if (chain == NULL) {
+ trace(LOG_ALWAYS, "Unhandled exception %d: %s", error, errno_to_str(error));
+ exit(error);
+ }
+ strncpy(chain->msg, msg, sizeof(chain->msg));
+ longjmp(chain->env, error);
+}
+
diff --git a/exceptions.h b/exceptions.h
new file mode 100644
index 00000000..8c2a54f8
--- /dev/null
+++ b/exceptions.h
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Exception handling. Functionality is similar to C++ try/catch.
+ * Usage example:
+ Trap trap;
+ if (set_trap(&trap)) {
+ // Some code that can throw an exception by calling exception()
+ ...
+
+ clear_trap(&trap);
+ }
+ else {
+ // Exception handling code
+ if (trap.error == ...
+ ...
+ }
+ * Only main thread is allowed to use exceptions.
+ */
+
+#ifndef D_exceptions
+#define D_exceptions
+
+#include <setjmp.h>
+#include "errors.h"
+
+typedef struct Trap Trap;
+
+struct Trap {
+ int error;
+ char msg[256];
+ jmp_buf env;
+ Trap * next;
+};
+
+#define set_trap(trap) (set_trap_a(trap), (trap)->error = setjmp((trap)->env), set_trap_b(trap))
+
+extern int set_trap_a(Trap * trap);
+extern int set_trap_b(Trap * trap);
+
+extern void clear_trap(Trap * trap);
+extern void exception(int error);
+extern void str_exception(int error, char * msg);
+
+#endif
diff --git a/expressions.c b/expressions.c
new file mode 100644
index 00000000..fa943578
--- /dev/null
+++ b/expressions.c
@@ -0,0 +1,854 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Expression evaluation library.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include "mdep.h"
+#include "myalloc.h"
+#include "exceptions.h"
+#include "expressions.h"
+
+#define STR_POOL_SIZE 1024
+
+struct StringValue {
+ struct StringValue * next;
+ char buf[1];
+};
+
+typedef struct StringValue StringValue;
+
+#define SY_LEQ 256
+#define SY_GEQ 257
+#define SY_EQU 258
+#define SY_NEQ 259
+#define SY_AND 260
+#define SY_OR 261
+#define SY_SHL 262
+#define SY_SHR 263
+#define SY_VAL 264
+#define SY_ID 265
+#define SY_REF 266
+#define SY_DEC 267
+#define SY_INC 268
+#define SY_A_SUB 269
+#define SY_A_ADD 270
+#define SY_A_SHL 271
+#define SY_A_SHR 272
+#define SY_A_OR 273
+#define SY_A_XOR 274
+#define SY_A_AND 275
+#define SY_A_MUL 276
+#define SY_A_DIV 277
+#define SY_A_MOD 278
+
+static ExpressionContext * expr_ctx = NULL;
+static char * text = NULL;
+static int text_pos = 0;
+static int text_ch = 0;
+static int text_sy = 0;
+static Value text_val;
+static char text_error[256];
+
+static char str_pool[STR_POOL_SIZE];
+static int str_pool_cnt = 0;
+static StringValue * str_alloc_list = NULL;
+
+static char * alloc_str(int len) {
+ if (str_pool_cnt + len < STR_POOL_SIZE) {
+ char * s = str_pool + str_pool_cnt;
+ str_pool_cnt += len + 1;
+ return s;
+ }
+ else {
+ StringValue * s = (StringValue *)loc_alloc(sizeof(StringValue) + len);
+ s->next = str_alloc_list;
+ str_alloc_list = s;
+ return s->buf;
+ }
+}
+
+void string_value(Value * v, char * str) {
+ memset(v, 0, sizeof(Value));
+ v->type = VALUE_STR;
+ if (str != NULL) {
+ int len = strlen(str);
+ v->str = alloc_str(len);
+ memcpy(v->str, str, len + 1);
+ }
+}
+
+static void error(int no, char * msg) {
+ snprintf(text_error, sizeof(text_error),
+ "Expression evaluation error: %s, text pos %d", msg, text_pos);
+ exception(no);
+}
+
+static void next_ch(void) {
+ text_ch = (unsigned char)text[text_pos];
+ if (text_ch != 0) text_pos++;
+}
+
+static int next_hex(void) {
+ int ch = text_ch;
+ next_ch();
+ if (ch >= '0' && ch <= '9') return ch - '0';
+ if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
+ if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
+ error(ERR_INV_EXPRESSION, "Invalid hexadecimal number");
+ return 0;
+}
+
+static int next_oct(void) {
+ int ch = text_ch;
+ next_ch();
+ if (ch >= '0' && ch <= '7') return ch - '0';
+ error(ERR_INV_EXPRESSION, "Invalid octal number");
+ return 0;
+}
+
+static int next_dec(void) {
+ int ch = text_ch;
+ next_ch();
+ if (ch >= '0' && ch <= '9') return ch - '0';
+ error(ERR_INV_EXPRESSION, "Invalid decimal number");
+ return 0;
+}
+
+static int next_char_val(void) {
+ int n = 0;
+ if (text_ch == '\\') {
+ next_ch();
+ switch (text_ch) {
+ case 'n' : n = '\n'; break;
+ case 't' : n = '\t'; break;
+ case 'v' : n = '\v'; break;
+ case 'b' : n = '\b'; break;
+ case 'r' : n = '\r'; break;
+ case 'f' : n = '\f'; break;
+ case 'a' : n = '\a'; break;
+ case '\\': n = '\\'; break;
+ case '\'': n = '\''; break;
+ case '"' : n = '"'; break;
+ case 'x' :
+ next_ch();
+ n = next_hex() << 8;
+ n |= next_hex() << 4;
+ n |= next_hex();
+ return n;
+ case '0' :
+ case '1' :
+ case '2' :
+ case '3' :
+ n = next_oct() << 6;
+ n |= next_oct() << 3;
+ n |= next_oct();
+ return n;
+ default :
+ n = text_ch;
+ break;
+ }
+ }
+ else {
+ n = text_ch;
+ }
+ next_ch();
+ return n;
+}
+
+static int is_name_character(int ch) {
+ if (ch >= 'A' && ch <= 'Z') return 1;
+ if (ch >= 'a' && ch <= 'z') return 1;
+ if (ch >= '0' && ch <= '9') return 1;
+ if (ch == '_') return 1;
+ if (ch == '$') return 1;
+ if (ch == '@') return 1;
+ return 0;
+}
+
+static void next_sy(void) {
+ for (;;) {
+ int ch = text_ch;
+ next_ch();
+ switch (ch) {
+ case 0:
+ text_sy = 0;
+ return;
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ continue;
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '~':
+ case '[':
+ case ']':
+ case ';':
+ case ':':
+ case '?':
+ case ',':
+ case '.':
+ text_sy = ch;
+ return;
+ case '-':
+ if (text_ch == '>') {
+ next_ch();
+ text_sy = SY_REF;
+ return;
+ }
+ if (text_ch == '-') {
+ next_ch();
+ text_sy = SY_DEC;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_SUB;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '+':
+ if (text_ch == '+') {
+ next_ch();
+ text_sy = SY_INC;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_ADD;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '<':
+ if (text_ch == '<') {
+ next_ch();
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_SHL;
+ return;
+ }
+ text_sy = SY_SHL;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_LEQ;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '>':
+ if (text_ch == '>') {
+ next_ch();
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_SHR;
+ return;
+ }
+ text_sy = SY_SHR;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_GEQ;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '=':
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_EQU;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '!':
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_NEQ;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '&':
+ if (text_ch == '&') {
+ next_ch();
+ text_sy = SY_AND;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_AND;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '|':
+ if (text_ch == '|') {
+ next_ch();
+ text_sy = SY_OR;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_OR;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '*':
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_MUL;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '/':
+ if (text_ch == '|') {
+ next_ch();
+ text_sy = SY_A_DIV;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '%':
+ if (text_ch == '|') {
+ next_ch();
+ text_sy = SY_A_MOD;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '^':
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_XOR;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '\'':
+ text_val.type = VALUE_UNS;
+ text_val.str = NULL;
+ text_val.value = next_char_val();
+ if (text_ch != '\'') error(ERR_INV_EXPRESSION, "Missing 'single quote'");
+ next_ch();
+ text_sy = SY_VAL;
+ return;
+ case '"':
+ {
+ int len = 0;
+ int cnt = 0;
+ int pos = text_pos;
+ while (text_ch != '"') {
+ next_char_val();
+ len++;
+ }
+ text_val.type = VALUE_STR;
+ text_val.value = 0;
+ text_val.str = alloc_str(len);
+ text_pos = pos - 1;
+ next_ch();
+ while (text_ch != '"') {
+ text_val.str[cnt++] = next_char_val();
+ }
+ assert(cnt == len);
+ text_val.str[cnt] = 0;
+ next_ch();
+ text_sy = SY_VAL;
+ }
+ return;
+ case '0':
+ if (text_ch == 'x') {
+ next_ch();
+ text_val.type = VALUE_UNS;
+ text_val.str = NULL;
+ text_val.value = 0;
+ while (text_ch >= '0' && text_ch <= '9' ||
+ text_ch >= 'A' && text_ch <= 'F' ||
+ text_ch >= 'a' && text_ch <= 'f') {
+ text_val.value = (text_val.value << 4) | next_hex();
+ }
+ }
+ else {
+ text_val.type = VALUE_INT;
+ text_val.str = NULL;
+ text_val.value = 0;
+ while (text_ch >= '0' && text_ch <= '7') {
+ text_val.value = (text_val.value << 3) | next_oct();
+ }
+ }
+ text_sy = SY_VAL;
+ return;
+ default:
+ if (ch >= '0' && ch <= '9') {
+ text_val.type = VALUE_INT;
+ text_val.str = NULL;
+ text_val.value = ch - '0';
+ while (text_ch >= '0' && text_ch <= '9') {
+ text_val.value = (text_val.value * 10) + next_dec();
+ }
+ text_sy = SY_VAL;
+ return;
+ }
+ if (is_name_character(ch)) {
+ int len = 1;
+ int cnt = 0;
+ int pos = text_pos - 1;
+ while (is_name_character(text_ch)) {
+ len++;
+ next_ch();
+ }
+ text_val.type = VALUE_STR;
+ text_val.value = 0;
+ text_val.str = alloc_str(len);
+ text_pos = pos - 1;
+ next_ch();
+ while (is_name_character(text_ch)) {
+ text_val.str[cnt++] = text_ch;
+ next_ch();
+ }
+ assert(cnt == len);
+ text_val.str[cnt] = 0;
+ text_sy = SY_ID;
+ return;
+ }
+ error(ERR_INV_EXPRESSION, "Illegal character");
+ break;
+ }
+ }
+}
+
+static int to_boolean(Value * v) {
+ if (v == NULL) return 0;
+ switch (v->type) {
+ case VALUE_INT: return v->value != 0;
+ case VALUE_UNS: return v->value != 0;
+ case VALUE_STR: return v->str != NULL;
+ default: assert(0);
+ }
+ return 0;
+}
+
+static int to_int(Value * v) {
+ if (v == NULL) return 0;
+ switch (v->type) {
+ case VALUE_INT: return v->value;
+ case VALUE_UNS: return v->value;
+ case VALUE_STR: return v->str != NULL;
+ default: assert(0);
+ }
+ return 0;
+}
+
+static unsigned to_uns(Value * v) {
+ if (v == NULL) return 0;
+ switch (v->type) {
+ case VALUE_INT: return (unsigned)v->value;
+ case VALUE_UNS: return (unsigned)v->value;
+ case VALUE_STR: return v->str != NULL;
+ default: assert(0);
+ }
+ return 0;
+}
+
+static void expression(Value * v);
+
+static void primary_expression(Value * v) {
+ if (text_sy == '(') {
+ next_sy();
+ expression(v);
+ if (text_sy != ')') error(ERR_INV_EXPRESSION, "Missing ')'");
+ next_sy();
+ }
+ else if (text_sy == SY_VAL) {
+ if (v) *v = text_val;
+ next_sy();
+ }
+ else if (text_sy == SY_ID) {
+ errno = 0;
+ if (expr_ctx->identifier == NULL || expr_ctx->identifier(text_val.str, v)) {
+ char msg[256];
+ int err = ERR_INV_EXPRESSION;
+ if (errno != 0) {
+ err = errno;
+ snprintf(msg, sizeof(msg), "Can't evaluate '%s': %d %s",
+ text_val.str, err, errno_to_str(err));
+ }
+ else {
+ snprintf(msg, sizeof(msg), "Undeclared identifier '%s'", text_val.str);
+ }
+ error(err, msg);
+ }
+ next_sy();
+ }
+ else {
+ error(ERR_INV_EXPRESSION, "Syntax error");
+ }
+}
+
+static void postfix_expression(Value * v) {
+ // TODO: postfix_expression()
+ primary_expression(v);
+}
+
+static void unary_expression(Value * v) {
+ // TODO: unary_expression()
+ postfix_expression(v);
+}
+
+static void cast_expression(Value * v) {
+ // TODO: cast_expression()
+ unary_expression(v);
+}
+
+static void multiplicative_expression(Value * v) {
+ cast_expression(v);
+ while (text_sy == '*' || text_sy == '/' || text_sy == '%') {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ cast_expression(v ? &x : NULL);
+ if (v) {
+ if (v->type == VALUE_STR || x.type == VALUE_STR) {
+ error(ERR_INV_EXPRESSION, "Operation is not applicable to string");
+ }
+ if (sy != '*' && x.value == 0) {
+ error(ERR_INV_EXPRESSION, "Dividing by zero");
+ }
+ if (v->type == VALUE_UNS || x.type == VALUE_UNS) {
+ switch (sy) {
+ case '*': v->value = to_uns(v) * to_uns(&x); break;
+ case '/': v->value = to_uns(v) / to_uns(&x); break;
+ case '%': v->value = to_uns(v) % to_uns(&x); break;
+ }
+ v->type = VALUE_UNS;
+ }
+ else {
+ switch (sy) {
+ case '*': v->value = to_int(v) * to_int(&x); break;
+ case '/': v->value = to_int(v) / to_int(&x); break;
+ case '%': v->value = to_int(v) % to_int(&x); break;
+ }
+ v->type = VALUE_INT;
+ }
+ }
+ }
+}
+
+static void additive_expression(Value * v) {
+ multiplicative_expression(v);
+ while (text_sy == '+' || text_sy == '-') {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ multiplicative_expression(v ? &x : NULL);
+ if (v) {
+ if (v->type == VALUE_STR && x.type == VALUE_STR) {
+ char * s;
+ if (sy != '+') error(ERR_INV_EXPRESSION, "Operation is not applicable to string");
+ if (v->str == NULL) {
+ v->str = x.str;
+ }
+ else if (x.str != NULL) {
+ s = alloc_str(strlen(v->str) + strlen(x.str));
+ strcpy(s, v->str);
+ strcat(s, x.str);
+ v->str = s;
+ }
+ }
+ else if (v->type == VALUE_STR || x.type == VALUE_STR) {
+ error(ERR_INV_EXPRESSION, "Operation is not applicable to string");
+ }
+ else if (v->type == VALUE_UNS || x.type == VALUE_UNS) {
+ switch (sy) {
+ case '+': v->value = to_uns(v) + to_uns(&x); break;
+ case '-': v->value = to_uns(v) - to_uns(&x); break;
+ }
+ v->type = VALUE_UNS;
+ }
+ else {
+ switch (sy) {
+ case '+': v->value = to_int(v) + to_int(&x); break;
+ case '-': v->value = to_int(v) - to_int(&x); break;
+ }
+ v->type = VALUE_INT;
+ }
+ }
+ }
+}
+
+static void shift_expression(Value * v) {
+ additive_expression(v);
+ while (text_sy == SY_SHL || text_sy == SY_SHR) {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ additive_expression(v ? &x : NULL);
+ if (v) {
+ if (v->type == VALUE_STR || x.type == VALUE_STR) {
+ error(ERR_INV_EXPRESSION, "Integral types expected");
+ }
+ if (x.type == VALUE_INT && to_int(&x) < 0) {
+ if (v->type == VALUE_UNS) {
+ switch (sy) {
+ case SY_SHL: v->value = to_uns(v) >> -to_int(&x); break;
+ case SY_SHR: v->value = to_uns(v) << -to_int(&x); break;
+ }
+ }
+ else {
+ switch (sy) {
+ case SY_SHL: v->value = to_int(v) >> -to_int(&x); break;
+ case SY_SHR: v->value = to_int(v) << -to_int(&x); break;
+ }
+ }
+ }
+ else {
+ if (v->type == VALUE_UNS) {
+ switch (sy) {
+ case SY_SHL: v->value = to_uns(v) << to_uns(&x); break;
+ case SY_SHR: v->value = to_uns(v) >> to_uns(&x); break;
+ }
+ }
+ else {
+ switch (sy) {
+ case SY_SHL: v->value = to_int(v) << to_uns(&x); break;
+ case SY_SHR: v->value = to_int(v) >> to_uns(&x); break;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void relational_expression(Value * v) {
+ shift_expression(v);
+ while (text_sy == '<' || text_sy == '>' || text_sy == SY_LEQ || text_sy == SY_GEQ) {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ shift_expression(v ? &x : NULL);
+ if (v) {
+ if (v->type == VALUE_STR && x.type == VALUE_STR) {
+ int n = 0;
+ if (v->str == NULL && x.str == NULL) n = 0;
+ else if (v->str == NULL) n = -1;
+ else if (x.str == NULL) n = 1;
+ else n = strcmp(v->str, x.str);
+ switch (sy) {
+ case '<': v->value = n < 0; break;
+ case '>': v->value = n > 0; break;
+ case SY_LEQ: v->value = n <= 0; break;
+ case SY_GEQ: v->value = n >= 0; break;
+ }
+ }
+ else if (v->type == VALUE_UNS || x.type == VALUE_UNS) {
+ switch (sy) {
+ case '<': v->value = to_uns(v) < to_uns(&x); break;
+ case '>': v->value = to_uns(v) > to_uns(&x); break;
+ case SY_LEQ: v->value = to_uns(v) <= to_uns(&x); break;
+ case SY_GEQ: v->value = to_uns(v) >= to_uns(&x); break;
+ }
+ }
+ else {
+ switch (sy) {
+ case '<': v->value = to_int(v) < to_int(&x); break;
+ case '>': v->value = to_int(v) > to_int(&x); break;
+ case SY_LEQ: v->value = to_int(v) <= to_int(&x); break;
+ case SY_GEQ: v->value = to_int(v) >= to_int(&x); break;
+ }
+ }
+ v->str = NULL;
+ v->type = VALUE_INT;
+ }
+ }
+}
+
+static void equality_expression(Value * v) {
+ relational_expression(v);
+ while (text_sy == SY_EQU || text_sy == SY_NEQ) {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ relational_expression(v ? &x : NULL);
+ if (v) {
+ if (v->type == VALUE_STR && x.type == VALUE_STR) {
+ if (v->str == NULL && x.str == NULL) v->value = 1;
+ else if (v->str == NULL || x.str == NULL) v->value = 0;
+ else v->value = strcmp(v->str, x.str) == 0;
+ }
+ else {
+ v->value = to_int(v) == to_int(&x);
+ }
+ v->str = NULL;
+ v->type = VALUE_INT;
+ if (sy == SY_NEQ) v->value = !v->value;
+ }
+ }
+}
+
+static void and_expression(Value * v) {
+ equality_expression(v);
+ while (text_sy == '&') {
+ Value x;
+ next_sy();
+ equality_expression(v ? &x : NULL);
+ if (v) {
+ v->value = to_int(v) & to_int(&x);
+ v->str = NULL;
+ v->type = v->type == VALUE_UNS || x.type == VALUE_UNS ? VALUE_UNS : VALUE_INT;
+ }
+ }
+}
+
+static void exclusive_or_expression(Value * v) {
+ and_expression(v);
+ while (text_sy == '^') {
+ Value x;
+ next_sy();
+ and_expression(v ? &x : NULL);
+ if (v) {
+ v->value = to_int(v) ^ to_int(&x);
+ v->str = NULL;
+ v->type = v->type == VALUE_UNS || x.type == VALUE_UNS ? VALUE_UNS : VALUE_INT;
+ }
+ }
+}
+
+static void inclusive_or_expression(Value * v) {
+ exclusive_or_expression(v);
+ while (text_sy == '|') {
+ Value x;
+ next_sy();
+ exclusive_or_expression(v ? &x : NULL);
+ if (v) {
+ v->value = to_int(v) | to_int(&x);
+ v->str = NULL;
+ v->type = v->type == VALUE_UNS || x.type == VALUE_UNS ? VALUE_UNS : VALUE_INT;
+ }
+ }
+}
+
+static void logical_and_expression(Value * v) {
+ inclusive_or_expression(v);
+ while (text_sy == SY_AND) {
+ Value x;
+ int b = to_boolean(v);
+ next_sy();
+ inclusive_or_expression(v && b ? &x : NULL);
+ if (v && b) *v = x;
+ }
+}
+
+static void logical_or_expression(Value * v) {
+ logical_and_expression(v);
+ while (text_sy == SY_OR) {
+ Value x;
+ int b = to_boolean(v);
+ next_sy();
+ logical_and_expression(v && !b ? &x : NULL);
+ if (v && !b) *v = x;
+ }
+}
+
+static void conditional_expression(Value * v) {
+ logical_or_expression(v);
+ if (text_sy == '?') {
+ Value x;
+ Value y;
+ int b = to_boolean(v);
+ next_sy();
+ expression(v && b ? &x : NULL);
+ if (text_sy != ':') error(ERR_INV_EXPRESSION, "Missing ':'");
+ next_sy();
+ conditional_expression(v && !b ? &y : NULL);
+ if (v) *v = b ? x : y;
+ }
+}
+
+static void expression(Value * v) {
+ // TODO: assignments in expressions
+ conditional_expression(v);
+}
+
+int evaluate_expression(ExpressionContext * ctx, char * s, Value * v) {
+ int r = 0;
+ Trap trap;
+ if (set_trap(&trap)) {
+ expr_ctx = ctx;
+ text_error[0] = 0;
+ text_val.str = NULL;
+ str_pool_cnt = 0;
+ while (str_alloc_list != NULL) {
+ StringValue * str = str_alloc_list;
+ str_alloc_list = str->next;
+ loc_free(str);
+ }
+ text = s;
+ text_pos = 0;
+ next_ch();
+ next_sy();
+ expression(v);
+ if (text_sy != 0) error(ERR_INV_EXPRESSION, "Illegal characters at the end of expression");
+ clear_trap(&trap);
+ }
+ else {
+ errno = trap.error;
+ r = -1;
+ }
+ return r;
+}
+
+char * get_expression_error_msg(void) {
+ if (text_error[0] == 0) return NULL;
+ return text_error;
+}
+
+void ini_expression_library(void) {
+#ifndef NDEBUG
+ Value v;
+ ExpressionContext ctx = { NULL, NULL };
+ assert(evaluate_expression(&ctx, "0", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 0 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "0.", &v) != 0);
+ assert(evaluate_expression(&ctx, "2 * 2", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 4 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "1 ? 2 : 3", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 2 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "0 ? 2 : 3", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 3 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "(1?2:3) == 2 && (0?2:3) == 3", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 1 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "(1?2:3) != 2 || (0?2:3) != 3", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 0 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "5>2 && 4<6", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 1 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "5<=2 || 4>=6", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 0 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "((5*2+7-1)/2)>>1==4 && 1<<3==8 && 5%2==1", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 1 && v.str == NULL);
+ assert(evaluate_expression(&ctx, "\042ABC\042 + \042DEF\042 == \042ABCDEF\042", &v) == 0);
+ assert(v.type == VALUE_INT && v.value == 1 && v.str == NULL);
+#endif
+}
+
diff --git a/expressions.h b/expressions.h
new file mode 100644
index 00000000..8fcb43c1
--- /dev/null
+++ b/expressions.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Expression evaluation library.
+ */
+
+#ifndef D_expression
+#define D_expression
+
+#define VALUE_INT 1
+#define VALUE_UNS 2
+#define VALUE_STR 3
+
+struct Value {
+ int type;
+ int value;
+ char * str;
+};
+
+typedef struct Value Value;
+
+struct ExpressionContext {
+ int (*identifier)(char *, Value *);
+ int (*type_cast)(char *, Value *);
+};
+
+typedef struct ExpressionContext ExpressionContext;
+
+extern int evaluate_expression(ExpressionContext * ctx, char * s, Value * v);
+
+extern char * get_expression_error_msg(void);
+
+extern void string_value(Value * v, char * str);
+
+extern void ini_expression_library(void);
+
+#endif
+
diff --git a/filesystem.c b/filesystem.c
new file mode 100644
index 00000000..e8dbb287
--- /dev/null
+++ b/filesystem.c
@@ -0,0 +1,1103 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Target service implementation: file system access (TCF name FileSystem)
+ */
+
+#include "config.h"
+#if SERVICE_FileSystem
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#ifdef WIN32
+# include <sys/utime.h>
+# include <direct.h>
+#else
+# include <utime.h>
+# include <dirent.h>
+#endif
+#include "mdep.h"
+#include "myalloc.h"
+#include "streams.h"
+#include "channel.h"
+#include "link.h"
+#include "trace.h"
+#include "json.h"
+#include "exceptions.h"
+#include "base64.h"
+#include "protocol.h"
+#include "filesystem.h"
+
+#define BUF_SIZE 0x1000
+
+static const char * FILE_SYSTEM = "FileSystem";
+
+static const int
+ TCF_O_READ = 0x00000001,
+ TCF_O_WRITE = 0x00000002,
+ TCF_O_APPEND = 0x00000004,
+ TCF_O_CREAT = 0x00000008,
+ TCF_O_TRUNC = 0x00000010,
+ TCF_O_EXCL = 0x00000020;
+
+static const int
+ ATTR_SIZE = 0x00000001,
+ ATTR_UIDGID = 0x00000002,
+ ATTR_PERMISSIONS = 0x00000004,
+ ATTR_ACMODTIME = 0x00000008;
+
+static const int
+ STATUS_OK = 0,
+ STATUS_EOF = 1,
+ STATUS_NO_SUCH_FILE = 2,
+ STATUS_PERMISSION_DENIED = 3,
+ STATUS_FAILURE = 4,
+ STATUS_BAD_MESSAGE = 5,
+ STATUS_NO_CONNECTION = 6,
+ STATUS_CONNECTION_LOST = 7,
+ STATUS_OP_UNSUPPORTED = 8;
+
+typedef struct OpenFileInfo OpenFileInfo;
+
+struct OpenFileInfo {
+ unsigned long handle;
+ char path[FILE_PATH_SIZE];
+ int file;
+ DIR * dir;
+ InputStream * inp;
+ LINK link_ring;
+ LINK link_hash;
+};
+
+#define hash2file(A) ((OpenFileInfo *)((char *)(A) - (int)&((OpenFileInfo *)0)->link_hash))
+#define ring2file(A) ((OpenFileInfo *)((char *)(A) - (int)&((OpenFileInfo *)0)->link_ring))
+
+typedef struct FileAttrs FileAttrs;
+
+struct FileAttrs {
+ int flags;
+ int64 size;
+ int uid;
+ int gid;
+ int permissions;
+ int64 atime;
+ int64 mtime;
+};
+
+static unsigned long handle_cnt = 0;
+
+#define HANDLE_HASH_SIZE 0x1000
+static LINK handle_hash[HANDLE_HASH_SIZE];
+static LINK file_info_ring = { NULL, NULL };
+
+#if defined(_WRS_KERNEL)
+# define FS_ROOT "host:c:/"
+#endif
+
+static OpenFileInfo * create_open_file_info(InputStream * inp, char * path, int file, DIR * dir) {
+ int i = 0;
+ LINK * list_head = NULL;
+
+ OpenFileInfo * h = (OpenFileInfo *)loc_alloc_zero(sizeof(OpenFileInfo));
+ for (;;) {
+ LINK * list_next;
+ OpenFileInfo * p = NULL;
+ h->handle = handle_cnt++;
+ list_head = &handle_hash[h->handle % HANDLE_HASH_SIZE];
+ for (list_next = list_head->next; list_next != list_head; list_next = list_next->next) {
+ if (hash2file(list_next)->handle == h->handle) {
+ p = hash2file(list_next);
+ break;
+ }
+ }
+ if (p == NULL) break;
+ }
+ strcpy(h->path, path);
+ h->file = file;
+ h->dir = dir;
+ h->inp = inp;
+ list_add_first(&h->link_ring, &file_info_ring);
+ list_add_first(&h->link_hash, list_head);
+ return h;
+}
+
+static OpenFileInfo * find_open_file_info(char * id) {
+ unsigned long handle = 0;
+ LINK * list_head = NULL;
+ LINK * list_next = NULL;
+
+ if (id == NULL || id[0] != 'F' || id[1] != 'S' || id[2] == 0) return NULL;
+ handle = strtoul(id + 2, &id, 10);
+ if (id[0] != 0) return NULL;
+ list_head = &handle_hash[handle % HANDLE_HASH_SIZE];
+ for (list_next = list_head->next; list_next != list_head; list_next = list_next->next) {
+ if (hash2file(list_next)->handle == handle) return hash2file(list_next);
+ }
+ return NULL;
+}
+
+static void delete_open_file_info(OpenFileInfo * h) {
+ list_remove(&h->link_ring);
+ list_remove(&h->link_hash);
+ loc_free(h);
+}
+
+static void channel_close_listener(InputStream * inp, OutputStream * out) {
+ LINK list;
+ LINK * list_next = NULL;
+
+ list_init(&list);
+ for (list_next = file_info_ring.next; list_next != &file_info_ring; list_next = list_next->next) {
+ OpenFileInfo * h = ring2file(list_next);
+ if (h->inp == inp) {
+ trace(LOG_ALWAYS, "file handle left open by client: FS%d", h->handle);
+ list_remove(&h->link_hash);
+ if (h->dir != NULL) {
+ closedir(h->dir);
+ h->dir = NULL;
+ }
+ if (h->file >= 0) {
+ close(h->file);
+ h->file = -1;
+ }
+ list_add_last(&h->link_hash, &list);
+ }
+ }
+
+ while (!list_is_empty(&list)) delete_open_file_info(hash2file(list.next));
+}
+
+static void write_fs_errno(OutputStream * out, int err) {
+ char * msg = NULL;
+ int status = 0;
+ switch (err) {
+ case 0:
+ status = STATUS_OK;
+ break;
+ case ERR_EOF:
+ status = STATUS_EOF;
+ break;
+ case ENOENT:
+ status = STATUS_NO_SUCH_FILE;
+ break;
+ case EACCES:
+ status = STATUS_PERMISSION_DENIED;
+ break;
+ default:
+ status = STATUS_FAILURE;
+ break;
+ }
+ json_write_long(out, status);
+ out->write(out, 0);
+ if (err != 0) msg = errno_to_str(err);
+ json_write_string(out, msg);
+ out->write(out, 0);
+}
+
+static void write_file_handle(OutputStream * out, OpenFileInfo * h) {
+ if (h == NULL) {
+ write_string(out, "null");
+ }
+ else {
+ char s[32];
+ char * p = s + sizeof(s);
+ unsigned long n = h->handle;
+ *(--p) = 0;
+ do {
+ *(--p) = (char)(n % 10 + '0');
+ n = n / 10;
+ }
+ while (n != 0);
+ *(--p) = 'S';
+ *(--p) = 'F';
+ json_write_string(out, p);
+ }
+ out->write(out, 0);
+}
+
+static void fill_attrs(FileAttrs * attrs, struct_stat * buf) {
+ memset(attrs, 0, sizeof(FileAttrs));
+ attrs->flags |= ATTR_SIZE | ATTR_UIDGID | ATTR_PERMISSIONS | ATTR_ACMODTIME;
+ attrs->size = buf->st_size;
+ attrs->uid = buf->st_uid;
+ attrs->gid = buf->st_gid;
+ attrs->permissions = buf->st_mode;
+ attrs->atime = (int64)buf->st_atime * 1000;
+ attrs->mtime = (int64)buf->st_mtime * 1000;
+}
+
+static void read_file_attrs(InputStream * inp, char * nm, void * arg) {
+ FileAttrs * attrs = (FileAttrs *)arg;
+ if (strcmp(nm, "Size") == 0) {
+ attrs->size = json_read_int64(inp);
+ attrs->flags |= ATTR_SIZE;
+ }
+ else if (strcmp(nm, "UID") == 0) {
+ attrs->uid = (int)json_read_long(inp);
+ attrs->flags |= ATTR_UIDGID;
+ }
+ else if (strcmp(nm, "GID") == 0) {
+ attrs->gid = (int)json_read_long(inp);
+ attrs->flags |= ATTR_UIDGID;
+ }
+ else if (strcmp(nm, "Permissions") == 0) {
+ attrs->permissions = (int)json_read_long(inp);
+ attrs->flags |= ATTR_PERMISSIONS;
+ }
+ else if (strcmp(nm, "ATime") == 0) {
+ attrs->atime = json_read_int64(inp);
+ attrs->flags |= ATTR_ACMODTIME;
+ }
+ else if (strcmp(nm, "MTime") == 0) {
+ attrs->mtime = json_read_int64(inp);
+ attrs->flags |= ATTR_ACMODTIME;
+ }
+ else {
+ exception(ERR_JSON_SYNTAX);
+ }
+}
+
+static void write_file_attrs(OutputStream * out, FileAttrs * attrs) {
+ int cnt = 0;
+
+ if (attrs == NULL) {
+ write_stringz(out, "null");
+ return;
+ }
+
+ out->write(out, '{');
+ if (attrs->flags & ATTR_SIZE) {
+ json_write_string(out, "Size");
+ out->write(out, ':');
+ json_write_int64(out, attrs->size);
+ cnt++;
+ }
+ if (attrs->flags & ATTR_UIDGID) {
+ if (cnt) out->write(out, ',');
+ json_write_string(out, "UID");
+ out->write(out, ':');
+ json_write_long(out, attrs->uid);
+ out->write(out, ',');
+ json_write_string(out, "GID");
+ out->write(out, ':');
+ json_write_long(out, attrs->gid);
+ cnt++;
+ }
+ if (attrs->flags & ATTR_SIZE) {
+ if (cnt) out->write(out, ',');
+ json_write_string(out, "Permissions");
+ out->write(out, ':');
+ json_write_long(out, attrs->permissions);
+ cnt++;
+ }
+ if (attrs->flags & ATTR_ACMODTIME) {
+ if (cnt) out->write(out, ',');
+ json_write_string(out, "ATime");
+ out->write(out, ':');
+ json_write_int64(out, attrs->atime);
+ out->write(out, ',');
+ json_write_string(out, "MTime");
+ out->write(out, ':');
+ json_write_int64(out, attrs->mtime);
+ cnt++;
+ }
+ out->write(out, '}');
+}
+
+static int to_local_open_flags(int flags) {
+ int res = O_BINARY | O_LARGEFILE;
+ if (flags & TCF_O_READ) res |= O_RDONLY;
+ if (flags & TCF_O_WRITE) res |= O_WRONLY;
+ if (flags & TCF_O_APPEND) res |= O_APPEND;
+ if (flags & TCF_O_CREAT) res |= O_CREAT;
+ if (flags & TCF_O_TRUNC) res |= O_TRUNC;
+ if (flags & TCF_O_EXCL) res |= O_EXCL;
+ return res;
+}
+
+static void read_path(InputStream * inp, char * path, int size) {
+ int i = 0;
+ char buf[FILE_PATH_SIZE];
+ json_read_string(inp, path, size);
+ while (path[i] != 0) {
+ if (path[i] == '\\') path[i] = '/';
+ i++;
+ }
+#ifdef WIN32
+ if (path[0] != 0 && path[1] == ':' && path[2] == '/') return;
+#elif defined(_WRS_KERNEL)
+ if (strncmp(path, FS_ROOT, strlen(FS_ROOT)) == 0) return;
+#endif
+ if (path[0] == 0) {
+ strcpy(path, get_user_home());
+ }
+ else if (path[0] != '/') {
+ snprintf(buf, sizeof(buf), "%s/%s", get_user_home(), path);
+ strcpy(path, buf);
+ }
+}
+
+static void command_open(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ unsigned long flags = 0;
+ FileAttrs attrs;
+ int file = -1;
+ int err = 0;
+ OpenFileInfo * handle = NULL;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ flags = json_read_ulong(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ memset(&attrs, 0, sizeof(FileAttrs));
+ json_read_struct(inp, read_file_attrs, &attrs);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if ((attrs.flags & ATTR_PERMISSIONS) == 0) {
+ attrs.permissions = 0775;
+ }
+ file = open(path, to_local_open_flags(flags), attrs.permissions);
+
+ if (file < 0){
+ err = errno;
+ }
+ else {
+ handle = create_open_file_info(inp, path, file, NULL);
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ write_file_handle(out, handle);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_close(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ OpenFileInfo * h = NULL;
+ int err = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ h = find_open_file_info(id);
+ if (h == NULL) {
+ err = EBADF;
+ }
+ else if (h->dir != NULL) {
+ if (closedir(h->dir) < 0) {
+ err = errno;
+ }
+ else {
+ delete_open_file_info(h);
+ }
+ }
+ else {
+ if (close(h->file) < 0) {
+ err = errno;
+ }
+ else {
+ delete_open_file_info(h);
+ }
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_read(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ OpenFileInfo * h = NULL;
+ int err = 0;
+ int eof = 0;
+ int64 offset;
+ unsigned long len;
+ unsigned long cnt = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ offset = json_read_int64(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ len = json_read_ulong(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ h = find_open_file_info(id);
+ if (h == NULL) {
+ err = EBADF;
+ write_stringz(out, "null");
+ }
+ else {
+ char buf[BUF_SIZE + 4];
+ int rem = 0;
+ unsigned long chk = 0;
+ out->write(out, '"');
+ while (cnt < len) {
+ if (lseek(h->file, offset + cnt, SEEK_SET) == -1) {
+ assert(errno != 0);
+ err = errno;
+ break;
+ }
+ else {
+ int i, j;
+ int rd = read(h->file, buf + rem, BUF_SIZE < len - cnt ? BUF_SIZE : len - cnt);
+ if (rd < 0) {
+ assert(errno != 0);
+ err = errno;
+ break;
+ }
+ if (rd == 0) {
+ assert(cnt < len);
+ eof = 1;
+ break;
+ }
+ j = rem + rd;
+ rem = j % 3;
+ chk += write_base64(out, buf, j - rem);
+ for (i = 0; i < rem; i++) buf[i] = buf[j - rem + i];
+ cnt += rd;
+ }
+ }
+ chk += write_base64(out, buf, rem);
+ out->write(out, '"');
+ out->write(out, 0);
+ assert(chk == (cnt + 2) / 3 * 4);
+ }
+
+ assert(err || eof || cnt == len);
+ write_fs_errno(out, err);
+ json_write_boolean(out, eof);
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_write(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ OpenFileInfo * h = NULL;
+ int err = 0;
+ int64 offset;
+ unsigned long len = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ offset = json_read_int64(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != '"') exception(ERR_JSON_SYNTAX);
+
+ h = find_open_file_info(id);
+ if (h == NULL) err = EBADF;
+ for (;;) {
+ char buf[BUF_SIZE];
+ int rd = read_base64(inp, buf, sizeof(buf));
+ if (rd == 0) break;
+ if (err == 0 && lseek(h->file, offset + len, SEEK_SET) == -1) {
+ err = errno;
+ }
+ if (err == 0) {
+ int wr = write(h->file, buf, rd);
+ if (wr < 0) err = errno;
+ else if (wr < rd) err = ENOSPC;
+ }
+ len += rd;
+ }
+
+ if (inp->read(inp) != '"') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void write_stat_result(char * token, OutputStream * out, int err, struct_stat * buf) {
+ FileAttrs attrs;
+
+ if (err == 0) fill_attrs(&attrs, buf);
+ else memset(&attrs, 0, sizeof(attrs));
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ write_file_attrs(out, &attrs);
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_stat(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ struct_stat buf;
+ int err = 0;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ memset(&buf, 0, sizeof(buf));
+ if (stat(path, &buf) < 0) err = errno;
+
+ write_stat_result(token, out, err, &buf);
+}
+
+static void command_lstat(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ struct_stat buf;
+ int err = 0;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ memset(&buf, 0, sizeof(buf));
+ if (lstat(path, &buf) < 0) err = errno;
+
+ write_stat_result(token, out, err, &buf);
+}
+
+static void command_fstat(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ struct_stat buf;
+ OpenFileInfo * h = NULL;
+ int err = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ h = find_open_file_info(id);
+ memset(&buf, 0, sizeof(buf));
+ if (h == NULL) err = EBADF;
+ else if (fstat(h->file, &buf) < 0) err = errno;
+ write_stat_result(token, out, err, &buf);
+}
+
+static void command_setstat(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ FileAttrs attrs;
+ int err = 0;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ memset(&attrs, 0, sizeof(FileAttrs));
+ json_read_struct(inp, read_file_attrs, &attrs);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (attrs.flags & ATTR_SIZE) {
+ if (truncate(path, attrs.size) < 0) err = errno;
+ }
+#if !defined(WIN32) && !defined(_WRS_KERNEL)
+ if (attrs.flags & ATTR_UIDGID) {
+ if (chown(path, attrs.uid, attrs.gid) < 0) err = errno;
+ }
+#endif
+ if (attrs.flags & ATTR_PERMISSIONS) {
+ if (chmod(path, attrs.permissions) < 0) err = errno;
+ }
+ if (attrs.flags & ATTR_ACMODTIME) {
+ struct utimbuf buf;
+ buf.actime = (long)(attrs.atime / 1000);
+ buf.modtime = (long)(attrs.mtime / 1000);
+ if (utime(path, &buf) < 0) err = errno;
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_fsetstat(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ FileAttrs attrs;
+ OpenFileInfo * h = NULL;
+ int err = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ memset(&attrs, 0, sizeof(FileAttrs));
+ json_read_struct(inp, read_file_attrs, &attrs);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ h = find_open_file_info(id);
+ if (h == NULL) {
+ err = EBADF;
+ }
+ else {
+ if (attrs.flags & ATTR_SIZE) {
+ if (ftruncate(h->file, attrs.size) < 0) err = errno;
+ }
+#if defined(WIN32) || defined(_WRS_KERNEL)
+ if (attrs.flags & ATTR_PERMISSIONS) {
+ if (chmod(h->path, attrs.permissions) < 0) err = errno;
+ }
+#else
+ if (attrs.flags & ATTR_UIDGID) {
+ if (fchown(h->file, attrs.uid, attrs.gid) < 0) err = errno;
+ }
+ if (attrs.flags & ATTR_PERMISSIONS) {
+ if (fchmod(h->file, attrs.permissions) < 0) err = errno;
+ }
+#endif
+ if (attrs.flags & ATTR_ACMODTIME) {
+ struct utimbuf buf;
+ buf.actime = (long)(attrs.atime / 1000);
+ buf.modtime = (long)(attrs.mtime / 1000);
+ if (utime(h->path, &buf) < 0) err = errno;
+ }
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_opendir(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ DIR * dir = NULL;
+ int err = 0;
+ OpenFileInfo * handle = NULL;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ dir = opendir(path);
+ if (dir == NULL){
+ err = errno;
+ }
+ else {
+ handle = create_open_file_info(inp, path, -1, dir);
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ write_file_handle(out, handle);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_readdir(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ OpenFileInfo * h = NULL;
+ int err = 0;
+ int eof = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ h = find_open_file_info(id);
+ if (h == NULL || h->dir == NULL) {
+ write_stringz(out, "null");
+ err = EBADF;
+ }
+ else {
+ int cnt = 0;
+ out->write(out, '[');
+ while (cnt < 64) {
+ struct dirent * e;
+ char path[FILE_PATH_SIZE];
+ struct_stat st;
+ FileAttrs attrs;
+ errno = 0;
+ e = readdir(h->dir);
+ if (e == NULL) {
+ err = errno;
+ if (err == 0) eof = 1;
+ break;
+ }
+ if (strcmp(e->d_name, ".") == 0) continue;
+ if (strcmp(e->d_name, "..") == 0) continue;
+ if (cnt > 0) out->write(out, ',');
+ out->write(out, '{');
+ json_write_string(out, "FileName");
+ out->write(out, ':');
+ json_write_string(out, e->d_name);
+ memset(&st, 0, sizeof(st));
+ snprintf(path, sizeof(path), "%s/%s", h->path, e->d_name);
+ if (stat(path, &st) == 0) {
+ fill_attrs(&attrs, &st);
+ out->write(out, ',');
+ json_write_string(out, "Attrs");
+ out->write(out, ':');
+ write_file_attrs(out, &attrs);
+ }
+ out->write(out, '}');
+ cnt++;
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+ }
+
+ write_fs_errno(out, err);
+ json_write_boolean(out, eof);
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_remove(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ DIR * dir = NULL;
+ int err = 0;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (remove(path) < 0) err = errno;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_rmdir(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ DIR * dir = NULL;
+ int err = 0;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (rmdir(path) < 0) err = errno;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_mkdir(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ FileAttrs attrs;
+ int err = 0;
+ int mode = 0777;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ memset(&attrs, 0, sizeof(FileAttrs));
+ json_read_struct(inp, read_file_attrs, &attrs);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (attrs.flags & ATTR_PERMISSIONS) {
+ mode = attrs.permissions;
+ }
+#if defined(WIN32) || defined(_WRS_KERNEL)
+ if (mkdir(path) < 0) err = errno;
+#else
+ if (mkdir(path, mode) < 0) err = errno;
+#endif
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_realpath(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ char * real = NULL;
+ int err = 0;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ real = canonicalize_file_name(path);
+ if (real == NULL) err = errno;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ json_write_string(out, real);
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+ free(real);
+}
+
+static void command_rename(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ char newp[FILE_PATH_SIZE];
+ int err = 0;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ read_path(inp, newp, sizeof(newp));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (rename(path, newp) < 0) err = errno;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_readlink(char * token, InputStream * inp, OutputStream * out) {
+ char path[FILE_PATH_SIZE];
+ char link[FILE_PATH_SIZE];
+ int err = 0;
+
+ read_path(inp, path, sizeof(path));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ link[0] = 0;
+#if defined(WIN32) || defined(_WRS_KERNEL)
+ err = ENOSYS;
+#else
+ if (readlink(path, link, sizeof(link)) < 0) err = errno;
+#endif
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ json_write_string(out, link);
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_symlink(char * token, InputStream * inp, OutputStream * out) {
+ char link[FILE_PATH_SIZE];
+ char target[FILE_PATH_SIZE];
+ int err = 0;
+
+ read_path(inp, link, sizeof(link));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ read_path(inp, target, sizeof(target));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+#if defined(WIN32) || defined(_WRS_KERNEL)
+ err = ENOSYS;
+#else
+ if (symlink(target, link) < 0) err = errno;
+#endif
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_copy(char * token, InputStream * inp, OutputStream * out) {
+ char src[FILE_PATH_SIZE];
+ char dst[FILE_PATH_SIZE];
+ int copy_uidgid;
+ int copy_perms;
+ struct_stat st;
+ int fi = -1;
+ int fo = -1;
+ int err = 0;
+ int64 pos = 0;
+
+ read_path(inp, src, sizeof(src));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ read_path(inp, dst, sizeof(dst));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ copy_uidgid = json_read_boolean(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ copy_perms = json_read_boolean(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (stat(src, &st) < 0) err = errno;
+ if (err == 0 && (fi = open(src, O_RDONLY | O_BINARY, 0)) < 0) err = errno;
+ if (err == 0 && (fo = open(dst, O_WRONLY | O_BINARY | O_CREAT, 0775)) < 0) err = errno;
+
+ while (err == 0 && pos < st.st_size) {
+ char buf[BUF_SIZE];
+ int wr = 0;
+ int rd = read(fi, buf, sizeof(buf));
+ if (rd == 0) break;
+ if (rd < 0) {
+ err == errno;
+ break;
+ }
+ wr = write(fo, buf, rd);
+ if (wr < 0) {
+ err = errno;
+ break;
+ }
+ if (wr < rd) {
+ err = ENOSPC;
+ break;
+ }
+ pos += rd;
+ }
+
+ if (fo >= 0 && close(fo) < 0 && err == 0) err = errno;
+ if (fi >= 0 && close(fi) < 0 && err == 0) err = errno;
+
+ if (err == 0) {
+ struct utimbuf buf;
+ buf.actime = st.st_atime;
+ buf.modtime = st.st_mtime;
+ if (utime(dst, &buf) < 0) err = errno;
+ }
+ if (err == 0 && copy_perms && chmod(dst, st.st_mode) < 0) err = errno;
+#if !defined(WIN32) && !defined(_WRS_KERNEL)
+ if (err == 0 && copy_uidgid && chown(dst, st.st_uid, st.st_gid) < 0) err = errno;
+#endif
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_user(char * token, InputStream * inp, OutputStream * out) {
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ json_write_long(out, getuid());
+ out->write(out, 0);
+ json_write_long(out, geteuid());
+ out->write(out, 0);
+ json_write_long(out, getgid());
+ out->write(out, 0);
+ json_write_long(out, getegid());
+ out->write(out, 0);
+ json_write_string(out, get_user_home());
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_roots(char * token, InputStream * inp, OutputStream * out) {
+ struct_stat st;
+ int err = 0;
+ int cnt = 0;
+ int disk = 0;
+
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ out->write(out, '[');
+
+#ifdef WIN32
+ for (disk = 'A'; disk <= 'Z'; disk++) {
+ char path[32];
+ snprintf(path, sizeof(path), "%c:/", disk);
+ memset(&st, 0, sizeof(st));
+ if (stat(path, &st) == 0) {
+ FileAttrs attrs;
+ if (cnt > 0) out->write(out, ',');
+ out->write(out, '{');
+ json_write_string(out, "FileName");
+ out->write(out, ':');
+ json_write_string(out, path);
+ fill_attrs(&attrs, &st);
+ out->write(out, ',');
+ json_write_string(out, "Attrs");
+ out->write(out, ':');
+ write_file_attrs(out, &attrs);
+ out->write(out, '}');
+ cnt++;
+ }
+ }
+#elif defined(_WRS_KERNEL)
+ out->write(out, '{');
+ json_write_string(out, "FileName");
+ out->write(out, ':');
+ json_write_string(out, FS_ROOT);
+ memset(&st, 0, sizeof(st));
+ if (stat("/", &st) == 0) {
+ FileAttrs attrs;
+ fill_attrs(&attrs, &st);
+ out->write(out, ',');
+ json_write_string(out, "Attrs");
+ out->write(out, ':');
+ write_file_attrs(out, &attrs);
+ }
+ out->write(out, '}');
+#else
+ out->write(out, '{');
+ json_write_string(out, "FileName");
+ out->write(out, ':');
+ json_write_string(out, "/");
+ memset(&st, 0, sizeof(st));
+ if (stat("/", &st) == 0) {
+ FileAttrs attrs;
+ fill_attrs(&attrs, &st);
+ out->write(out, ',');
+ json_write_string(out, "Attrs");
+ out->write(out, ':');
+ write_file_attrs(out, &attrs);
+ }
+ out->write(out, '}');
+#endif
+
+ out->write(out, ']');
+ out->write(out, 0);
+ write_fs_errno(out, err);
+
+ out->write(out, MARKER_EOM);
+}
+
+void ini_file_system_service(void) {
+ int i;
+
+ add_channel_close_listener(channel_close_listener);
+ list_init(&file_info_ring);
+ for (i = 0; i < HANDLE_HASH_SIZE; i++) {
+ list_init(&handle_hash[i]);
+ }
+
+ add_command_handler(FILE_SYSTEM, "open", command_open);
+ add_command_handler(FILE_SYSTEM, "close", command_close);
+ add_command_handler(FILE_SYSTEM, "read", command_read);
+ add_command_handler(FILE_SYSTEM, "write", command_write);
+ add_command_handler(FILE_SYSTEM, "stat", command_stat);
+ add_command_handler(FILE_SYSTEM, "lstat", command_lstat);
+ add_command_handler(FILE_SYSTEM, "fstat", command_fstat);
+ add_command_handler(FILE_SYSTEM, "setstat", command_setstat);
+ add_command_handler(FILE_SYSTEM, "fsetstat", command_fsetstat);
+ add_command_handler(FILE_SYSTEM, "opendir", command_opendir);
+ add_command_handler(FILE_SYSTEM, "readdir", command_readdir);
+ add_command_handler(FILE_SYSTEM, "remove", command_remove);
+ add_command_handler(FILE_SYSTEM, "rmdir", command_rmdir);
+ add_command_handler(FILE_SYSTEM, "mkdir", command_mkdir);
+ add_command_handler(FILE_SYSTEM, "realpath", command_realpath);
+ add_command_handler(FILE_SYSTEM, "rename", command_rename);
+ add_command_handler(FILE_SYSTEM, "readlink", command_readlink);
+ add_command_handler(FILE_SYSTEM, "symlink", command_symlink);
+ add_command_handler(FILE_SYSTEM, "copy", command_copy);
+ add_command_handler(FILE_SYSTEM, "user", command_user);
+ add_command_handler(FILE_SYSTEM, "roots", command_roots);
+}
+
+#endif
+
diff --git a/filesystem.h b/filesystem.h
new file mode 100644
index 00000000..5e8b5085
--- /dev/null
+++ b/filesystem.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Target service implementation: file system access (TCF name FileSystem)
+ */
+
+#ifndef D_filesystem
+#define D_filesystem
+
+/*
+ * Initialize file system service.
+ */
+extern void ini_file_system_service(void);
+
+#endif
diff --git a/json.c b/json.c
new file mode 100644
index 00000000..6a85cec1
--- /dev/null
+++ b/json.c
@@ -0,0 +1,489 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module provides support for JSON - a computer data interchange format.
+ * It is a text-based, human-readable format for representing simple data structures and
+ * associative arrays (called objects). The JSON format is specified in RFC 4627 by Douglas Crockford.
+ * JSON is TCF preffered marshaling format.
+ */
+
+#include "json.h"
+#include "assert.h"
+#include "myalloc.h"
+#include "exceptions.h"
+
+static char * buf = NULL;
+static unsigned buf_size = 0;
+
+void json_write_ulong(OutputStream * out, unsigned long n) {
+ if (n >= 10) {
+ json_write_ulong(out, n / 10);
+ n = n % 10;
+ }
+ out->write(out, n + '0');
+}
+
+void json_write_long(OutputStream * out, long n) {
+ if (n < 0) {
+ out->write(out, '-');
+ n = -n;
+ }
+ json_write_ulong(out, (unsigned long)n);
+}
+
+void json_write_int64(OutputStream * out, int64 n) {
+ if (n < 0) {
+ out->write(out, '-');
+ n = -n;
+ if (n < 0) exception(EINVAL);
+ }
+ if (n >= 10) {
+ json_write_int64(out, n / 10);
+ n = n % 10;
+ }
+ out->write(out, (int)n + '0');
+}
+
+void json_write_boolean(OutputStream * out, int b) {
+ if (b) write_string(out, "true");
+ else write_string(out, "false");
+}
+
+static char hex_digit(unsigned n) {
+ n &= 0xf;
+ if (n < 10) return '0' + n;
+ return 'A' + (n - 10);
+}
+
+void json_write_char(OutputStream * out, char ch) {
+ unsigned n = ch & 0xff;
+ if (n < ' ') {
+ out->write(out, '\\');
+ out->write(out, 'u');
+ out->write(out, '0');
+ out->write(out, '0');
+ out->write(out, hex_digit(n >> 4));
+ out->write(out, hex_digit(n));
+ }
+ else {
+ if (n == '"' || n == '\\') out->write(out, '\\');
+ out->write(out, n);
+ }
+}
+
+void json_write_string(OutputStream * out, const char * str) {
+ if (str == NULL) {
+ write_string(out, "null");
+ }
+ else {
+ out->write(out, '"');
+ while (*str) json_write_char(out, *str++);
+ out->write(out, '"');
+ }
+}
+
+void json_write_string_len(OutputStream * out, const char * str, size_t len) {
+ if (str == NULL) {
+ write_string(out, "null");
+ }
+ else {
+ out->write(out, '"');
+ while (len > 0) {
+ json_write_char(out, *str++);
+ len--;
+ }
+ out->write(out, '"');
+ }
+}
+
+static int readHex(InputStream * inp) {
+ int ch = inp->read(inp);
+ if (ch >= '0' && ch <= '9') return ch - '0';
+ if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
+ if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
+ exception(ERR_JSON_SYNTAX);
+ return 0;
+}
+
+static int readHexChar(InputStream * inp) {
+ int n = readHex(inp) << 12;
+ n |= readHex(inp) << 8;
+ n |= readHex(inp) << 4;
+ n |= readHex(inp);
+ return n;
+}
+
+static int read_esc_char(InputStream * inp) {
+ int ch = inp->read(inp);
+ switch (ch) {
+ case '"': break;
+ case '\\': break;
+ case '/': break;
+ case 'b': ch = '\b'; break;
+ case 'f': ch = '\f'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'u': ch = readHexChar(inp); break;
+ default: exception(ERR_JSON_SYNTAX);
+ }
+ return ch;
+}
+
+int json_read_string(InputStream * inp, char * str, size_t size) {
+ unsigned i = 0;
+ int ch = inp->read(inp);
+ if (ch == 'n') {
+ if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ str[0] = 0;
+ return -1;
+ }
+ if (ch != '"') exception(ERR_JSON_SYNTAX);
+ for (;;) {
+ ch = inp->read(inp);
+ if (ch == '"') break;
+ if (ch == '\\') ch = read_esc_char(inp);
+ if (i < size - 1) str[i] = (char)ch;
+ i++;
+ }
+ if (i < size) str[i] = 0;
+ else str[size - 1] = 0;
+ return i;
+}
+
+char * json_read_alloc_string(InputStream * inp) {
+ char * str = NULL;
+ unsigned i = 0;
+ int ch = inp->read(inp);
+ if (ch == 'n') {
+ if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ return NULL;
+ }
+ if (ch != '"') exception(ERR_JSON_SYNTAX);
+ for (;;) {
+ ch = inp->read(inp);
+ if (ch == '"') break;
+ if (ch == '\\') ch = read_esc_char(inp);
+ if (i >= buf_size) {
+ int new_size = buf_size == 0 ? 0x1000 : buf_size * 2;
+ char * tmp = (char *)loc_alloc(new_size);
+ if (i > 0) memcpy(tmp, buf, i);
+ if (buf != NULL) loc_free(buf);
+ buf = tmp;
+ buf_size = new_size;
+ }
+ buf[i++] = (char)ch;
+ }
+ str = (char *)loc_alloc(i + 1);
+ memcpy(str, buf, i);
+ str[i] = 0;
+ return str;
+}
+
+int json_read_boolean(InputStream * inp) {
+ int ch = inp->read(inp);
+ if (ch == 'f') {
+ if (inp->read(inp) != 'a') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 's') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'e') exception(ERR_JSON_SYNTAX);
+ return 0;
+ }
+ if (ch == 't') {
+ if (inp->read(inp) != 'r') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'e') exception(ERR_JSON_SYNTAX);
+ return 1;
+ }
+ exception(ERR_JSON_SYNTAX);
+ return 0;
+}
+
+long json_read_long(InputStream * inp) {
+ long res = 0;
+ int neg = 0;
+ int ch = inp->read(inp);
+ if (ch == '-') {
+ neg = 1;
+ ch = inp->read(inp);
+ }
+ if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX);
+ res = ch - '0';
+ while (1) {
+ ch = inp->peek(inp);
+ if (ch < '0' || ch > '9') break;
+ inp->read(inp);
+ res = res * 10 + (ch - '0');
+ }
+ if (neg) return -res;
+ return res;
+}
+
+unsigned long json_read_ulong(InputStream * inp) {
+ unsigned long res = 0;
+ int neg = 0;
+ int ch = inp->read(inp);
+ if (ch == '-') {
+ neg = 1;
+ ch = inp->read(inp);
+ }
+ if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX);
+ res = ch - '0';
+ while (1) {
+ ch = inp->peek(inp);
+ if (ch < '0' || ch > '9') break;
+ inp->read(inp);
+ res = res * 10 + (ch - '0');
+ }
+ if (neg) return ~res + 1;
+ return res;
+}
+
+int64 json_read_int64(InputStream * inp) {
+ int64 res = 0;
+ int neg = 0;
+ int ch = inp->read(inp);
+ if (ch == '-') {
+ neg = 1;
+ ch = inp->read(inp);
+ }
+ if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX);
+ res = ch - '0';
+ while (1) {
+ ch = inp->peek(inp);
+ if (ch < '0' || ch > '9') break;
+ inp->read(inp);
+ res = res * 10 + (ch - '0');
+ }
+ if (neg) return -res;
+ return res;
+}
+
+int json_read_struct(InputStream * inp, struct_call_back call_back, void * arg) {
+ int ch = inp->read(inp);
+ if (ch == 'n') {
+ if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ return 0;
+ }
+ if (ch == '{') {
+ ch = inp->read(inp);
+ if (ch != '}') {
+ for (;;) {
+ int nm_len = 0;
+ char nm[256];
+ if (ch != '"') exception(ERR_JSON_SYNTAX);
+ for (;;) {
+ ch = inp->read(inp);
+ if (ch == '"') break;
+ if (ch == '\\') {
+ ch = inp->read(inp);
+ switch (ch) {
+ case '"': break;
+ case '\\': break;
+ case '/': break;
+ case 'b': ch = '\b'; break;
+ case 'f': ch = '\f'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'u': ch = readHexChar(inp); break;
+ default: exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (nm_len < sizeof(nm) - 1) {
+ nm[nm_len] = (char)ch;
+ nm_len++;
+ }
+ }
+ nm[nm_len] = 0;
+ ch = inp->read(inp);
+ if (ch != ':') exception(ERR_JSON_SYNTAX);
+ call_back(inp, nm, arg);
+ ch = inp->read(inp);
+ if (ch == '}') break;
+ if (ch != ',') exception(ERR_JSON_SYNTAX);
+ ch = inp->read(inp);
+ }
+ }
+ return 1;
+ }
+ exception(ERR_JSON_SYNTAX);
+ return 0;
+}
+
+char ** json_read_alloc_string_array(InputStream * inp, int * pos) {
+ int ch = inp->read(inp);
+ *pos = 0;
+ if (ch == 'n') {
+ if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ return NULL;
+ }
+ else if (ch != '[') {
+ exception(ERR_PROTOCOL);
+ return NULL;
+ }
+ else {
+ static int * len_buf = NULL;
+ static int len_buf_size = 0;
+
+ int buf_pos = 0;
+ int len_pos = 0;
+
+ int i, j;
+ char * str = NULL;
+ char ** arr = NULL;
+
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ int ch;
+ int len;
+ if (buf == NULL) {
+ buf_size = 0x1000;
+ buf = (char *)loc_alloc(buf_size);
+ }
+ else if (buf_size - buf_pos < 0x200) {
+ char * tmp = (char *)loc_alloc(buf_size * 2);
+ memcpy(tmp, buf, buf_pos);
+ loc_free(buf);
+ buf = tmp;
+ buf_size *= 2;
+ }
+ if (len_pos >= len_buf_size) {
+ len_buf_size = len_buf_size == 0 ? 0x100 : len_buf_size * 2;
+ len_buf = (int *)loc_realloc(len_buf, len_buf_size * sizeof(int));
+ }
+ len = json_read_string(inp, buf + buf_pos, buf_size - buf_pos);
+ if (len >= (int)(buf_size - buf_pos)) exception(ERR_BUFFER_OVERFLOW);
+ len_buf[len_pos++] = len;
+ buf_pos += len + 1;
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ arr = (char **)loc_alloc((len_pos + 1) * sizeof(char *) + buf_pos);
+ str = (char *)(arr + len_pos + 1);
+ memcpy(str, buf, buf_pos);
+ j = 0;
+ for (i = 0; i < len_pos; i++) {
+ arr[i] = str + j;
+ j += len_buf[i] + 1;
+ }
+ arr[len_pos] = NULL;
+ *pos = len_pos;
+ return arr;
+ }
+}
+
+void json_skip_object(InputStream * inp) {
+ int ch = inp->read(inp);
+ if (ch == 'n') {
+ if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
+ return;
+ }
+ if (ch == '"') {
+ for (;;) {
+ ch = inp->read(inp);
+ if (ch == '"') break;
+ if (ch == '\\') {
+ if (inp->read(inp) == 'u') readHexChar(inp);
+ }
+ }
+ return;
+ }
+ if (ch == '-' || ch >= '0' && ch <= '9') {
+ while (1) {
+ ch = inp->peek(inp);
+ if (ch < '0' || ch > '9') break;
+ inp->read(inp);
+ }
+ return;
+ }
+ if (ch == '[') {
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ int ch;
+ json_skip_object(inp);
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ return;
+ }
+ if (ch == '{') {
+ if (inp->peek(inp) == '}') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ int ch;
+ json_skip_object(inp);
+ if (inp->read(inp) != ':') exception(ERR_JSON_SYNTAX);
+ json_skip_object(inp);
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == '}') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ }
+ exception(ERR_JSON_SYNTAX);
+}
+
+void write_errno(OutputStream * out, int err) {
+ char * msg = NULL;
+ json_write_long(out, err);
+ out->write(out, 0);
+ if (err != 0) msg = errno_to_str(err);
+ json_write_string(out, msg);
+ out->write(out, 0);
+}
+
+void write_err_msg(OutputStream * out, int err, char * msg) {
+ json_write_long(out, err);
+ out->write(out, 0);
+ if (err == 0) {
+ write_string(out, "null");
+ }
+ else {
+ char * str = errno_to_str(err);
+ out->write(out, '"');
+ while (*str) json_write_char(out, *str++);
+ if (msg != NULL) {
+ out->write(out, ':');
+ out->write(out, ' ');
+ while (*msg) json_write_char(out, *msg++);
+ }
+ out->write(out, '"');
+ }
+ out->write(out, 0);
+}
+
diff --git a/json.h b/json.h
new file mode 100644
index 00000000..3c22789f
--- /dev/null
+++ b/json.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * This module provides support for JSON - a computer data interchange format.
+ * It is a text-based, human-readable format for representing simple data structures and
+ * associative arrays (called objects). The JSON format is specified in RFC 4627 by Douglas Crockford.
+ * JSON is TCF preffered marshaling format.
+ */
+
+#ifndef D_json
+#define D_json
+
+#include <stdlib.h>
+#include "mdep.h"
+#include "streams.h"
+
+extern int json_read_string(InputStream * inp, char * str, size_t size);
+extern int json_read_boolean(InputStream * inp);
+extern long json_read_long(InputStream * inp);
+extern unsigned long json_read_ulong(InputStream * inp);
+extern int64 json_read_int64(InputStream * inp);
+extern char * json_read_alloc_string(InputStream * inp);
+extern char ** json_read_alloc_string_array(InputStream * inp, int * len);
+
+typedef void (*struct_call_back)(InputStream *, char *, void *);
+extern int json_read_struct(InputStream * inp, struct_call_back call_back, void * arg);
+
+extern void json_skip_object(InputStream * inp);
+
+extern void json_write_ulong(OutputStream * out, unsigned long n);
+extern void json_write_long(OutputStream * out, long n);
+extern void json_write_int64(OutputStream * out, int64 n);
+extern void json_write_char(OutputStream * out, char ch);
+extern void json_write_string(OutputStream * out, const char * str);
+extern void json_write_string_len(OutputStream * out, const char * str, size_t len);
+extern void json_write_boolean(OutputStream * out, int b);
+
+extern void write_errno(OutputStream * out, int err);
+extern void write_err_msg(OutputStream * out, int err, char * msg);
+
+#endif
diff --git a/linenumbers.c b/linenumbers.c
new file mode 100644
index 00000000..19b5c0c4
--- /dev/null
+++ b/linenumbers.c
@@ -0,0 +1,641 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF service line Numbers
+ * The service associates locations in the source files with the corresponding
+ * machine instruction addresses in the executable object.
+ */
+
+#include "config.h"
+#if SERVICE_LineNumbers
+
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include "linenumbers.h"
+#include "context.h"
+#include "myalloc.h"
+#include "exceptions.h"
+#include "json.h"
+#include "protocol.h"
+#include "elf.h"
+#include "dwarfio.h"
+#include "dwarf.h"
+
+static const char * LINENUMBERS = "LineNumbers";
+
+typedef unsigned long ADDR_T;
+
+struct FileInfo {
+ char * name;
+ char * dir;
+ U4_T mtime;
+ U4_T size;
+};
+
+typedef struct FileInfo FileInfo;
+
+struct LineNumbersState {
+ unsigned file;
+ unsigned line;
+ unsigned column;
+ ADDR_T address;
+ U1_T isa;
+ U1_T is_stmt;
+ U1_T basic_block;
+ U1_T prologue_end;
+ U1_T epilogue_begin;
+ U1_T end_sequence;
+};
+
+typedef struct LineNumbersState LineNumbersState;
+
+struct CompUnit {
+ ADDR_T low_pc;
+ ADDR_T high_pc;
+ U8_T debug_ranges_offs;
+ U8_T debug_info_offs;
+ U8_T line_info_offs;
+ char * name;
+ char * dir;
+
+ U4_T files_cnt;
+ U4_T files_max;
+ FileInfo * files;
+
+ U4_T dirs_cnt;
+ U4_T dirs_max;
+ char ** dirs;
+
+ U4_T states_cnt;
+ U4_T states_max;
+ LineNumbersState * states;
+};
+
+typedef struct CompUnit CompUnit;
+
+struct LineNumbersCache {
+ ELF_File * file;
+ CompUnit * units;
+ U4_T units_max;
+ U4_T units_cnt;
+ ELF_Section * debug_ranges;
+ ELF_Section * debug_line;
+};
+
+typedef struct LineNumbersCache LineNumbersCache;
+
+static LineNumbersCache * read_cache;
+
+static void read_tag_com_unit(U2_T attr, U2_T form) {
+ static CompUnit * unit;
+ switch (attr) {
+ case 0:
+ if (form) {
+ if (read_cache->units_cnt >= read_cache->units_max) {
+ read_cache->units_max = read_cache->units_max == 0 ? 16 : read_cache->units_max * 2;
+ read_cache->units = (CompUnit *)loc_realloc(read_cache->units, sizeof(CompUnit) * read_cache->units_max);
+ }
+ unit = read_cache->units + read_cache->units_cnt++;
+ memset(unit, 0, sizeof(CompUnit));
+ unit->debug_ranges_offs = ~(U8_T)0;
+ }
+ else {
+ /* Skip to next compilation unit */
+ assert(dio_gUnitSize > 0);
+ dio_Skip(dio_gUnitPos + dio_gUnitSize - dio_GetPos());
+ }
+ break;
+ case AT_low_pc:
+ dio_ChkAddr(form);
+ unit->low_pc = (ADDR_T)dio_gFormRef;
+ break;
+ case AT_high_pc:
+ dio_ChkAddr(form);
+ unit->high_pc = (ADDR_T)dio_gFormRef;
+ break;
+ case AT_ranges:
+ dio_ChkData(form);
+ unit->debug_ranges_offs = dio_gFormData;
+ break;
+ case AT_name:
+ dio_ChkString(form);
+ unit->name = (char *)loc_alloc(dio_gFormBlockSize);
+ strcpy(unit->name, (char *)dio_gFormBlockBuf);
+ break;
+ case AT_comp_dir:
+ dio_ChkString(form);
+ unit->dir = (char *)loc_alloc(dio_gFormBlockSize);
+ strcpy(unit->dir, (char *)dio_gFormBlockBuf);
+ break;
+ case AT_stmt_list:
+ dio_ChkData(form);
+ unit->line_info_offs = dio_gFormData;
+ break;
+ }
+}
+
+static void entry_callback(U2_T Tag, U2_T attr, U2_T form) {
+ switch (Tag) {
+ case TAG_compile_unit :
+ read_tag_com_unit(attr, form);
+ break;
+ }
+}
+
+static void free_unit_cache(CompUnit * unit) {
+ U4_T j;
+
+ for (j = 0; j < unit->files_cnt; j++) {
+ loc_free(unit->files[j].name);
+ }
+ unit->files_cnt = 0;
+ unit->files_max = 0;
+ loc_free(unit->files);
+
+ for (j = 0; j < unit->dirs_cnt; j++) {
+ loc_free(unit->dirs[j]);
+ }
+ unit->dirs_cnt = 0;
+ unit->dirs_max = 0;
+ loc_free(unit->dirs);
+
+ unit->states_cnt = 0;
+ unit->states_max = 0;
+ loc_free(unit->states);
+}
+
+static void free_line_numbers_cache(ELF_File * file) {
+ LineNumbersCache * cache = (LineNumbersCache *)file->line_numbers_cache;
+ if (cache != NULL) {
+ U4_T i;
+ for (i = 0; i < cache->units_cnt; i++) {
+ CompUnit * unit = cache->units + i;
+ loc_free(unit->name);
+ loc_free(unit->dir);
+ free_unit_cache(unit);
+ }
+ loc_free(cache->units);
+ loc_free(cache);
+ }
+}
+
+static LineNumbersCache * get_line_numbers_cache(Context * ctx) {
+ ELF_File * file;
+ char fnm[FILE_PATH_SIZE];
+
+#if defined(WIN32)
+ exception(EINVAL);
+#elif defined(_WRS_KERNEL)
+ exception(EINVAL);
+#else
+ snprintf(fnm, sizeof(fnm), "/proc/%d/exe", ctx->mem);
+#endif
+
+ file = elf_open(fnm);
+ if (file == NULL) exception(errno);
+ if (file->line_numbers_cache == NULL) {
+ Trap trap;
+ if (set_trap(&trap)) {
+ unsigned idx;
+ LineNumbersCache * cache = NULL;
+ dio_LoadAbbrevTable(file);
+ cache = (LineNumbersCache *)(file->line_numbers_cache = loc_alloc_zero(sizeof(LineNumbersCache)));
+ cache->file = file;
+ read_cache = cache;
+ for (idx = 0; idx < file->section_cnt; idx++) {
+ ELF_Section * sec = file->sections[idx];
+ if (sec == NULL) continue;
+ if (sec->size == 0) continue;
+ if (sec->name == NULL) continue;
+ if (strcmp(sec->name, ".debug") == 0 || strcmp(sec->name, ".debug_info") == 0) {
+ dio_EnterSection(sec, 0);
+ dio_gVersion = strcmp(sec->name, ".debug") == 0 ? 1 : 2;
+ while (dio_GetPos() < sec->size) dio_ReadUnit(entry_callback);
+ dio_ExitSection();
+ }
+ else if (strcmp(sec->name, ".debug_ranges") == 0) {
+ cache->debug_ranges = sec;
+ }
+ else if (strcmp(sec->name, ".debug_line") == 0) {
+ cache->debug_line = sec;
+ }
+ }
+ read_cache = NULL;
+ clear_trap(&trap);
+ }
+ else {
+ free_line_numbers_cache(file);
+ str_exception(trap.error, trap.msg);
+ }
+ }
+ return (LineNumbersCache *)file->line_numbers_cache;
+}
+
+static void add_dir(CompUnit * unit, char * name) {
+ if (unit->dirs_cnt >= unit->dirs_max) {
+ unit->dirs_max = unit->dirs_max == 0 ? 16 : unit->dirs_max * 2;
+ unit->dirs = (char **)loc_realloc(unit->dirs, sizeof(char *) * unit->dirs_max);
+ }
+ unit->dirs[unit->dirs_cnt++] = name;
+}
+
+static void add_file(CompUnit * unit, FileInfo * file) {
+ if (unit->files_cnt >= unit->files_max) {
+ unit->files_max = unit->files_max == 0 ? 16 : unit->files_max * 2;
+ unit->files = (FileInfo *)loc_realloc(unit->files, sizeof(FileInfo) * unit->files_max);
+ }
+ if (file->dir == NULL) file->dir = unit->dir;
+ unit->files[unit->files_cnt++] = *file;
+}
+
+static void add_state(CompUnit * unit, LineNumbersState * state) {
+ if (unit->states_cnt >= unit->states_max) {
+ unit->states_max = unit->states_max == 0 ? 128 : unit->states_max * 2;
+ unit->states = (LineNumbersState *)loc_realloc(unit->states, sizeof(LineNumbersState) * unit->states_max);
+ }
+ unit->states[unit->states_cnt++] = *state;
+}
+
+static void load_line_numbers(LineNumbersCache * cache, CompUnit * unit) {
+ Trap trap;
+ if (unit->files != NULL && unit->dirs != NULL) return;
+ dio_gUnitPos = unit->line_info_offs;
+ dio_EnterSection(cache->debug_line, dio_gUnitPos);
+ if (set_trap(&trap)) {
+ U8_T header_pos = 0;
+ U1_T opcode_base = 0;
+ U1_T opcode_size[256];
+ U4_T header_size = 0;
+ U1_T min_instruction_length = 0;
+ U1_T is_stmt_default = 0;
+ I1_T line_base = 0;
+ U1_T line_range = 0;
+ U4_T unit_size = 0;
+ LineNumbersState state;
+
+ // Read header
+ unit_size = dio_ReadU4();
+ if (unit_size == 0xffffffffu) {
+ str_exception(ERR_DWARF, "64-bit DWARF is not supported yet");
+ }
+ else {
+ unit_size += 4;
+ }
+ dio_ReadU2(); /* line info version */
+ header_size = dio_ReadU4();
+ header_pos = dio_GetPos();
+ min_instruction_length = dio_ReadU1();
+ is_stmt_default = dio_ReadU1() != 0;
+ line_base = (I1_T)dio_ReadU1();
+ line_range = dio_ReadU1();
+ opcode_base = dio_ReadU1();
+ memset(opcode_size, 0, sizeof(opcode_size));
+ dio_Read(opcode_size + 1, opcode_base - 1);
+
+ // Read directory names
+ for (;;) {
+ char * name = dio_ReadString();
+ if (name == NULL) break;
+ add_dir(unit, name);
+ }
+
+ // Read source files info
+ for (;;) {
+ U4_T dir = 0;
+ FileInfo file;
+ memset(&file, 0, sizeof(file));
+ file.name = dio_ReadString();
+ if (file.name == NULL) break;
+ dir = dio_ReadLEB128();
+ if (dir > 0 && dir <= unit->dirs_cnt) file.dir = unit->dirs[dir - 1];
+ file.mtime = dio_ReadLEB128();
+ file.size = dio_ReadLEB128();
+ add_file(unit, &file);
+ }
+
+ // Run the program
+ if (header_pos + header_size != dio_GetPos())
+ str_exception(ERR_DWARF, "Invalid line info header");
+ memset(&state, 0, sizeof(state));
+ state.file = 1;
+ state.line = 1;
+ state.is_stmt = is_stmt_default;
+ while (dio_GetPos() < dio_gUnitPos + unit_size) {
+ U1_T opcode = dio_ReadU1();
+ if (opcode >= opcode_base) {
+ state.line += (unsigned)((int)((opcode - opcode_base) % line_range) + line_base);
+ state.address += (opcode - opcode_base) / line_range * min_instruction_length;
+ add_state(unit, &state);
+ state.basic_block = 0;
+ state.prologue_end = 0;
+ state.epilogue_begin = 0;
+ }
+ else if (opcode == 0) {
+ U4_T op_size = dio_ReadLEB128();
+ U8_T op_pos = dio_GetPos();
+ switch (dio_ReadU1()) {
+ case DW_LNE_define_file: {
+ U4_T dir = 0;
+ FileInfo file;
+ memset(&file, 0, sizeof(file));
+ file.name = dio_ReadString();
+ dir = dio_ReadLEB128();
+ if (dir > 0 && dir <= unit->dirs_cnt) file.dir = unit->dirs[dir - 1];
+ file.mtime = dio_ReadLEB128();
+ file.size = dio_ReadLEB128();
+ add_file(unit, &file);
+ break;
+ }
+ case DW_LNE_end_sequence:
+ state.end_sequence = 1;
+ add_state(unit, &state);
+ memset(&state, 0, sizeof(state));
+ state.file = 1;
+ state.line = 1;
+ state.is_stmt = is_stmt_default;
+ break;
+ case DW_LNE_set_address:
+ state.address = (ADDR_T)dio_ReadAddress();
+ break;
+ default:
+ dio_Skip(op_size - 1);
+ break;
+ }
+ assert(dio_GetPos() == op_pos + op_size);
+ }
+ else {
+ switch (opcode) {
+ case DW_LNS_copy:
+ add_state(unit, &state);
+ state.basic_block = 0;
+ state.prologue_end = 0;
+ state.epilogue_begin = 0;
+ break;
+ case DW_LNS_advance_pc:
+ state.address += (ADDR_T)(dio_ReadU8LEB128() * min_instruction_length);
+ break;
+ case DW_LNS_advance_line:
+ state.line += dio_ReadLEB128();
+ break;
+ case DW_LNS_set_file:
+ state.file = dio_ReadLEB128();
+ break;
+ case DW_LNS_set_column:
+ state.column = dio_ReadLEB128();
+ break;
+ case DW_LNS_negate_stmt:
+ state.is_stmt = !state.is_stmt;
+ break;
+ case DW_LNS_set_basic_block:
+ state.basic_block = 1;
+ break;
+ case DW_LNS_const_add_pc:
+ state.address += (255 - opcode_base) / line_range * min_instruction_length;
+ break;
+ case DW_LNS_fixed_advance_pc:
+ state.address += dio_ReadU2();
+ break;
+ case DW_LNS_set_prologue_end:
+ state.prologue_end = 1;
+ break;
+ case DW_LNS_set_epilogue_begin:
+ state.epilogue_begin = 1;
+ break;
+ case DW_LNS_set_isa:
+ state.isa = (U1_T)dio_ReadLEB128();
+ break;
+ default:
+ str_exception(ERR_DWARF, "Invalid line info op code");
+ break;
+ }
+ }
+ }
+ dio_ExitSection();
+ clear_trap(&trap);
+ }
+ else {
+ dio_ExitSection();
+ free_unit_cache(unit);
+ str_exception(trap.error, trap.msg);
+ }
+}
+
+static CompUnit * find_unit(LineNumbersCache * cache, ADDR_T addr0, ADDR_T addr1, ADDR_T * addr_next) {
+ U4_T i;
+ CompUnit * unit = NULL;
+ ADDR_T low_pc = 0;
+ // TODO: faster unit search
+ for (i = 0; i < cache->units_cnt; i++) {
+ CompUnit * u = cache->units + i;
+ if (u->debug_ranges_offs != ~(U8_T)0) {
+ if (cache->debug_ranges != NULL) {
+ U8_T base = u->low_pc;
+ U8_T max = 0;
+ dio_gUnitPos = u->debug_ranges_offs;
+ dio_EnterSection(cache->debug_ranges, dio_gUnitPos);
+ while (1) {
+ U8_T x = dio_ReadAddress();
+ U8_T y = dio_ReadAddress();
+ if (x == 0 && y == 0) break;
+ if (x == ((U8_T)1 << dio_gAddressSize * 8) - 1) {
+ base = y;
+ }
+ else {
+ x = base + x;
+ y = base + y;
+ if (addr0 < y && addr1 > x) {
+ if (unit == NULL || low_pc > x) {
+ unit = u;
+ low_pc = (ADDR_T)x;
+ *addr_next = (ADDR_T)y;
+ }
+ }
+ }
+ }
+ dio_ExitSection();
+ }
+ }
+ else if (u->low_pc != 0 && u->high_pc != 0) {
+ if (addr0 < u->high_pc && addr1 > u->low_pc) {
+ if (unit == NULL || low_pc > u->low_pc) {
+ unit = u;
+ low_pc = u->low_pc;
+ *addr_next = u->high_pc;
+ }
+ }
+ }
+ }
+ return unit;
+}
+
+static void load_line_numbers_in_range(LineNumbersCache * cache, ADDR_T addr0, ADDR_T addr1) {
+ while (addr0 < addr1) {
+ ADDR_T next = 0;
+ CompUnit * unit = find_unit(cache, addr0, addr1, &next);
+ if (unit == NULL) break;
+ load_line_numbers(cache, unit);
+ addr0 = next;
+ }
+}
+
+static void command_map_to_source(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char * err_msg = NULL;
+ char id[256];
+ ADDR_T addr0;
+ ADDR_T addr1;
+ Context * ctx = NULL;
+ LineNumbersCache * cache = NULL;
+ Trap trap;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ addr0 = json_read_ulong(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ addr1 = json_read_ulong(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ ctx = id2ctx(id);
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (err == 0) {
+ if (set_trap(&trap)) {
+ cache = get_line_numbers_cache(ctx);
+ load_line_numbers_in_range(cache, addr0, addr1);
+ clear_trap(&trap);
+ }
+ else {
+ err = trap.error;
+ err_msg = trap.msg;
+ }
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_err_msg(out, err, err_msg);
+ if (err != 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ int cnt = 0;
+ FileInfo * file = NULL;
+ out->write(out, '[');
+ while (addr0 < addr1) {
+ U4_T i;
+ ADDR_T next = 0;
+ FileInfo * state_file = NULL;
+ CompUnit * unit = find_unit(cache, addr0, addr1, &next);
+ if (unit == NULL) break;
+
+ for (i = 0; i < unit->states_cnt - 1; i++) {
+ LineNumbersState * state = unit->states + i;
+ LineNumbersState * next = unit->states + i + 1;
+ if (state->end_sequence) continue;
+ if (next->address > addr0 && state->address < addr1) {
+ if (cnt > 0) out->write(out, ',');
+ out->write(out, '{');
+ json_write_string(out, "SLine");
+ out->write(out, ':');
+ json_write_ulong(out, state->line - 1);
+ if (state->column > 0) {
+ out->write(out, ',');
+ json_write_string(out, "SCol");
+ out->write(out, ':');
+ json_write_ulong(out, state->column - 1);
+ }
+ out->write(out, ',');
+ json_write_string(out, "ELine");
+ out->write(out, ':');
+ json_write_ulong(out, next->line - 1);
+ if (next->column > 0) {
+ out->write(out, ',');
+ json_write_string(out, "ECol");
+ out->write(out, ':');
+ json_write_ulong(out, next->column - 1);
+ }
+ state_file = NULL;
+ if (state->file >= 1 && state->file <= unit->files_cnt) {
+ state_file = unit->files + (state->file - 1);
+ }
+ if (file != state_file) {
+ file = state_file;
+ out->write(out, ',');
+ json_write_string(out, "File");
+ out->write(out, ':');
+ json_write_string(out, file == NULL ? NULL : file->name);
+ out->write(out, ',');
+ json_write_string(out, "Dir");
+ out->write(out, ':');
+ json_write_string(out, file == NULL ? NULL : file->dir);
+ }
+ out->write(out, ',');
+ json_write_string(out, "SAddr");
+ out->write(out, ':');
+ json_write_ulong(out, state->address);
+ out->write(out, ',');
+ json_write_string(out, "EAddr");
+ out->write(out, ':');
+ json_write_ulong(out, next->address);
+ if (state->isa != 0) {
+ out->write(out, ',');
+ json_write_string(out, "ISA");
+ out->write(out, ':');
+ json_write_ulong(out, state->isa);
+ }
+ if (state->is_stmt) {
+ out->write(out, ',');
+ json_write_string(out, "IsStmt");
+ out->write(out, ':');
+ json_write_boolean(out, state->is_stmt);
+ }
+ if (state->basic_block) {
+ out->write(out, ',');
+ json_write_string(out, "BasicBlock");
+ out->write(out, ':');
+ json_write_boolean(out, state->basic_block);
+ }
+ if (state->prologue_end) {
+ out->write(out, ',');
+ json_write_string(out, "PrologueEnd");
+ out->write(out, ':');
+ json_write_boolean(out, state->prologue_end);
+ }
+ if (state->epilogue_begin) {
+ out->write(out, ',');
+ json_write_string(out, "EpilogueBegin");
+ out->write(out, ':');
+ json_write_boolean(out, state->epilogue_begin);
+ }
+ out->write(out, '}');
+ cnt++;
+ }
+ }
+
+ addr0 = next;
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+ }
+ out->write(out, MARKER_EOM);
+ if (cache != NULL) elf_close(cache->file);
+}
+
+void ini_line_numbers_service(void) {
+ elf_add_close_listener(free_line_numbers_cache);
+ add_command_handler(LINENUMBERS, "mapToSource", command_map_to_source);
+}
+
+#endif
+
diff --git a/linenumbers.h b/linenumbers.h
new file mode 100644
index 00000000..fad5ef29
--- /dev/null
+++ b/linenumbers.h
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF service Line Numbers
+ * The service associates locations in the source files with the corresponding
+ * machine instruction addresses in the executable object.
+ */
+
+#ifndef D_linenumbers
+#define D_linenumbers
+
+/*
+ * Initialize Line Numbers service.
+ */
+extern void ini_line_numbers_service(void);
+
+
+#endif
diff --git a/link.h b/link.h
new file mode 100644
index 00000000..f994418b
--- /dev/null
+++ b/link.h
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Double linked list support.
+ */
+
+#ifndef D_link
+#define D_link
+
+typedef struct LINK LINK;
+
+struct LINK {
+ LINK * next;
+ LINK * prev;
+};
+
+#define list_init(list) (list)->next = (list)->prev = (list)
+
+#define list_is_empty(list) ((list)->next == (list) && (list)->prev == (list))
+
+#define list_remove(item) (item)->prev->next = (item)->next; \
+ (item)->next->prev = (item)->prev
+
+#define list_add_first(item,list) (item)->next = (list)->next; (item)->prev = (list); \
+ (list)->next->prev = (item); (list)->next = (item)
+
+#define list_add_last(item,list) (item)->next = (list); (item)->prev = (list)->prev; \
+ (list)->prev->next = (item); (list)->prev = (item)
+
+#define list_concat(item,list) if(!list_is_empty(list)) { \
+ (item)->prev->next = (list)->next; \
+ (list)->next->prev = (item)->prev; \
+ (item)->prev = (list)->prev; \
+ (list)->prev->next = (item); \
+ }
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 00000000..9d56f280
--- /dev/null
+++ b/main.c
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Agent main module.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include "mdep.h"
+#include "events.h"
+#include "trace.h"
+#include "expressions.h"
+#include "cmdline.h"
+#include "context.h"
+#include "channel.h"
+#include "protocol.h"
+#include "runctrl.h"
+#include "registers.h"
+#include "stacktrace.h"
+#include "memory.h"
+#include "breakpoints.h"
+#include "diagnostics.h"
+#include "filesystem.h"
+#include "processes.h"
+#include "symbols.h"
+#include "linenumbers.h"
+#include "proxy.h"
+#include "sysmon.h"
+
+static char * progname;
+
+#if defined(_WRS_KERNEL)
+int tcf(void) {
+#else
+int main(int argc, char **argv) {
+#endif
+ int c;
+ int ind;
+ int interactive = 0;
+ char *s;
+ char *log_name = 0;
+ int port = 1534;
+
+ ini_mdep();
+ ini_trace();
+ ini_events_queue();
+ ini_expression_library();
+
+#if defined(_WRS_KERNEL)
+
+ progname = "tcf";
+ log_file = stdout;
+ log_mode = 0;
+
+#else
+
+ progname = argv[0];
+
+ /* Parse arguments */
+ for (ind = 1; ind < argc; ind++) {
+ s = argv[ind];
+ if (*s != '-') {
+ break;
+ }
+ s++;
+ while ((c = *s++) != '\0') {
+ switch (c) {
+ case 'i':
+ interactive = 1;
+ break;
+
+ case 'l':
+ case 'L':
+ if (*s == '\0') {
+ if (++ind >= argc) {
+ fprintf(stderr, "%s: error: no argument given to option '%c'\n", progname, c);
+ exit(1);
+ }
+ s = argv[ind];
+ }
+ switch (c) {
+ case 'l':
+ log_mode = strtol(s, 0, 0);
+ break;
+
+ case 'L':
+ log_name = s;
+ break;
+
+ default:
+ fprintf(stderr, "%s: error: illegal option '%c'\n", progname, c);
+ exit(1);
+ }
+ s = "";
+ break;
+
+ default:
+ fprintf(stderr, "%s: error: illegal option '%c'\n", progname, c);
+ exit(1);
+ }
+ }
+ }
+
+ /* Create log file */
+ if (log_name == 0) {
+ log_file = NULL;
+ }
+ else if (strcmp(log_name, "-") == 0) {
+ log_file = stderr;
+ }
+ else if ((log_file = fopen(log_name, "a")) == NULL) {
+ fprintf(stderr, "%s: error: cannot create log file %s\n", progname, log_name);
+ exit(1);
+ }
+
+#endif
+
+ if (interactive) ini_cmdline_handler();
+
+ ini_protocol();
+#if SERVICE_RunControl
+ ini_run_ctrl_service();
+#endif
+#if SERVICE_Breakpoints
+ ini_breakpoints_service();
+#endif
+#if SERVICE_Memory
+ ini_memory_service();
+#endif
+#if SERVICE_Registers
+ ini_registers_service();
+#endif
+#if SERVICE_StackTrace
+ ini_stack_trace_service();
+#endif
+#if SERVICE_Symbols
+ ini_symbols_service();
+#endif
+#if SERVICE_LineNumbers
+ ini_line_numbers_service();
+#endif
+#if SERVICE_Processes
+ ini_processes_service();
+#endif
+#if SERVICE_FileSystem
+ ini_file_system_service();
+#endif
+#if SERVICE_SysMonitor
+ ini_sys_mon_service();
+#endif
+ ini_diagnostics_service();
+ ini_proxy_service();
+ ini_contexts();
+ ini_channel_manager(port);
+
+ /* Process events - must run on the initial thread since ptrace()
+ * returns ECHILD otherwise, thinking we are not the owner. */
+ run_event_loop();
+ return 0;
+}
diff --git a/mdep.c b/mdep.c
new file mode 100644
index 00000000..d4e7d127
--- /dev/null
+++ b/mdep.c
@@ -0,0 +1,621 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include "mdep.h"
+
+pthread_attr_t pthread_create_attr;
+
+#ifdef WIN32
+
+#include <fcntl.h>
+#include <shlobj.h>
+
+#define ERR_SOCKET (-1)
+#define ERR_WIN32 (-2)
+
+/*********************************************************************
+ Support of pthreads on Windows is implemented according to
+ reccomendations from the paper:
+
+ Strategies for Implementing POSIX Condition Variables on Win32
+ C++ Report, SIGS, Vol. 10, No. 5, June, 1998
+
+ Douglas C. Schmidt and Irfan Pyarali
+ Department of Computer Science
+ Washington University, St. Louis, Missouri
+**********************************************************************/
+
+void pthread_mutex_init(pthread_mutex_t * mutex, void * attr) {
+ assert(attr == NULL);
+ *mutex = CreateMutex(NULL, FALSE, NULL);
+ if (*mutex == NULL) {
+ fprintf(stderr, "Can't create mutex: error %d\n", GetLastError());
+ exit(1);
+ }
+}
+
+void pthread_mutex_lock(pthread_mutex_t * mutex) {
+ assert(mutex != NULL);
+ assert(*mutex != NULL);
+ WaitForSingleObject(*mutex, INFINITE);
+}
+
+void pthread_mutex_unlock(pthread_mutex_t * mutex) {
+ assert(mutex != NULL);
+ assert(*mutex != NULL);
+ ReleaseMutex(*mutex);
+}
+
+void pthread_cond_init(pthread_cond_t * cond, void * attr) {
+ assert(attr == NULL);
+ cond->waiters_count = 0;
+ cond->was_broadcast = 0;
+ cond->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
+ InitializeCriticalSection(&cond->waiters_count_lock);
+ cond->waiters_done = CreateEvent(NULL, FALSE, FALSE, NULL);
+}
+
+int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) {
+ DWORD res = 0;
+ int last_waiter = 0;
+
+ EnterCriticalSection(&cond->waiters_count_lock);
+ cond->waiters_count++;
+ LeaveCriticalSection(&cond->waiters_count_lock);
+
+ // This call atomically releases the mutex and waits on the
+ // semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
+ // are called by another thread.
+ res = SignalObjectAndWait(*mutex, cond->sema, INFINITE, FALSE);
+
+ // Reacquire lock to avoid race conditions.
+ EnterCriticalSection(&cond->waiters_count_lock);
+
+ // We're no longer waiting...
+ cond->waiters_count--;
+
+ // Check to see if we're the last waiter after <pthread_cond_broadcast>.
+ last_waiter = cond->was_broadcast && cond->waiters_count == 0;
+
+ LeaveCriticalSection(&cond->waiters_count_lock);
+
+ // If we're the last waiter thread during this particular broadcast
+ // then let all the other threads proceed.
+ if (last_waiter) {
+ // This call atomically signals the <waiters_done_> event and waits until
+ // it can acquire the <mutex>. This is required to ensure fairness.
+ SignalObjectAndWait(cond->waiters_done, *mutex, INFINITE, FALSE);
+ }
+ else {
+ // Always regain the external mutex since that's the guarantee we
+ // give to our callers.
+ WaitForSingleObject(*mutex, INFINITE);
+ }
+ assert(res == WAIT_OBJECT_0);
+ return 0;
+}
+
+int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, struct timespec * timeout) {
+ DWORD res = 0;
+ int last_waiter = 0;
+
+ EnterCriticalSection(&cond->waiters_count_lock);
+ cond->waiters_count++;
+ LeaveCriticalSection(&cond->waiters_count_lock);
+
+ // This call atomically releases the mutex and waits on the
+ // semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
+ // are called by another thread.
+ res = SignalObjectAndWait(*mutex, cond->sema, timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000, FALSE);
+
+ // Reacquire lock to avoid race conditions.
+ EnterCriticalSection(&cond->waiters_count_lock);
+
+ // We're no longer waiting...
+ cond->waiters_count--;
+
+ // Check to see if we're the last waiter after <pthread_cond_broadcast>.
+ last_waiter = cond->was_broadcast && cond->waiters_count == 0;
+
+ LeaveCriticalSection(&cond->waiters_count_lock);
+
+ // If we're the last waiter thread during this particular broadcast
+ // then let all the other threads proceed.
+ if (last_waiter) {
+ // This call atomically signals the <waiters_done> event and waits until
+ // it can acquire the <mutex>. This is required to ensure fairness.
+ SignalObjectAndWait(cond->waiters_done, *mutex, INFINITE, FALSE);
+ }
+ else {
+ // Always regain the external mutex since that's the guarantee we
+ // give to our callers.
+ WaitForSingleObject(*mutex, INFINITE);
+ }
+
+ if (res == WAIT_TIMEOUT) return errno = ETIMEDOUT;
+ assert(res == WAIT_OBJECT_0);
+ return 0;
+}
+
+void pthread_cond_signal(pthread_cond_t *cond) {
+ int have_waiters = 0;
+
+ EnterCriticalSection(&cond->waiters_count_lock);
+ have_waiters = cond->waiters_count > 0;
+ LeaveCriticalSection(&cond->waiters_count_lock);
+
+ // If there aren't any waiters, then this is a no-op.
+ if (have_waiters) ReleaseSemaphore(cond->sema, 1, 0);
+}
+
+void pthread_cond_broadcast(pthread_cond_t *cond) {
+ int have_waiters = 0;
+
+ // This is needed to ensure that <waiters_count_> and <was_broadcast_> are
+ // consistent relative to each other.
+ EnterCriticalSection(&cond->waiters_count_lock);
+
+ if (cond->waiters_count > 0) {
+ // We are broadcasting, even if there is just one waiter...
+ // Record that we are broadcasting, which helps optimize
+ // <pthread_cond_wait> for the non-broadcast case.
+ cond->was_broadcast = 1;
+ have_waiters = 1;
+ }
+
+ if (have_waiters) {
+ // Wake up all the waiters atomically.
+ ReleaseSemaphore(cond->sema, cond->waiters_count, 0);
+
+ LeaveCriticalSection(&cond->waiters_count_lock);
+
+ // Wait for all the awakened threads to acquire the counting
+ // semaphore.
+ WaitForSingleObject(cond->waiters_done, INFINITE);
+ // This assignment is okay, even without the <waiters_count_lock_> held
+ // because no other waiter threads can wake up to access it.
+ cond->was_broadcast = 0;
+ }
+ else {
+ LeaveCriticalSection(&cond->waiters_count_lock);
+ }
+}
+
+typedef struct ThreadArgs ThreadArgs;
+
+struct ThreadArgs {
+ void * (*start)(void *);
+ void * args;
+};
+
+static void start_thread(void * x) {
+ ThreadArgs a = *(ThreadArgs *)x;
+
+ free(x);
+ ExitThread((DWORD)a.start(a.args));
+}
+
+int pthread_create(pthread_t * thread, pthread_attr_t * attr,
+ void * (*start)(void *), void * args) {
+ unsigned long r;
+ ThreadArgs * a;
+
+ a = (ThreadArgs *)malloc(sizeof(ThreadArgs));
+ a->start = start;
+ a->args = args;
+ r = _beginthread(start_thread, 0, a);
+ if (r == (unsigned long)-1) {
+ int error = errno;
+ free(a);
+ errno = error;
+ return error;
+ }
+ *thread = (HANDLE)r;
+ return 0;
+}
+
+int pthread_join(pthread_t thread, void **value_ptr) {
+ if (WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) {
+ return EINVAL;
+ }
+ if (!GetExitCodeThread(thread, (LPDWORD)value_ptr)) {
+ return EINVAL;
+ }
+ CloseHandle(thread);
+ return 0;
+}
+
+pthread_t pthread_self(void) {
+ return GetCurrentThread();
+}
+
+static __int64 file_time_to_unix_time (const FILETIME * ft) {
+ __int64 res = (__int64)ft->dwHighDateTime << 32;
+
+ res |= ft->dwLowDateTime;
+ res /= 10; /* from 100 nano-sec periods to usec */
+ res -= 11644473600000000u; /* from Win epoch to Unix epoch */
+ return res;
+}
+
+int clock_gettime(clockid_t clock_id, struct timespec * tp) {
+ FILETIME ft;
+ __int64 tim;
+
+ assert(clock_id == CLOCK_REALTIME);
+ if (!tp) {
+ errno = EINVAL;
+ return -1;
+ }
+ GetSystemTimeAsFileTime(&ft);
+ tim = file_time_to_unix_time(&ft);
+ tp->tv_sec = (long)(tim / 1000000L);
+ tp->tv_nsec = (long)(tim % 1000000L) * 1000;
+ return 0;
+}
+
+void usleep(useconds_t useconds) {
+ Sleep(useconds / 1000);
+}
+
+void perror(const char * msg) {
+ int error = errno;
+ if (error == ERR_SOCKET) {
+ fprintf(stderr, "%s: socket error 0x%08x\n", msg, WSAGetLastError());
+ }
+ else if (error == ERR_WIN32) {
+ fprintf(stderr, "%s: Win32 error 0x%08x\n", msg, GetLastError());
+ }
+ else {
+ fprintf(stderr, "%s: %s\n", msg, strerror(error));
+ }
+}
+
+#undef bind
+int wsa_bind(int socket, const struct sockaddr * addr, int addr_size) {
+ int res = 0;
+ SetLastError(0);
+ WSASetLastError(0);
+ res = bind(socket, addr, addr_size);
+ if (res != 0) {
+ errno = ERR_SOCKET;
+ return -1;
+ }
+ return 0;
+}
+
+#undef socket
+int wsa_socket(int af, int type, int protocol) {
+ int res = 0;
+ SetLastError(0);
+ WSASetLastError(0);
+ res = socket(af, type, protocol);
+ if (res < 0) {
+ errno = ERR_SOCKET;
+ return -1;
+ }
+ return res;
+}
+
+int truncate(const char * path, int64 size) {
+ int res = 0;
+ int f = _open(path, _O_RDWR | _O_BINARY);
+ if (f < 0) return -1;
+ res = ftruncate(f, size);
+ _close(f);
+ return res;
+}
+
+int ftruncate(int fd, int64 size) {
+ int64 cur, pos;
+ BOOL ret = FALSE;
+ HANDLE handle = (HANDLE)_get_osfhandle(fd);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ errno = EBADF;
+ return -1;
+ }
+ /* save the current file pointer */
+ cur = _lseeki64(fd, 0, SEEK_CUR);
+ if (cur >= 0) {
+ pos = _lseeki64(fd, size, SEEK_SET);
+ if (pos >= 0) {
+ ret = SetEndOfFile(handle);
+ if (!ret) errno = EBADF;
+ }
+ /* restore the file pointer */
+ _lseeki64(fd, cur, SEEK_SET);
+ }
+ return ret ? 0 : -1;
+}
+
+DIR * opendir(const char *path) {
+ DIR * d = (DIR *)malloc(sizeof(DIR));
+ if (!d) { errno = ENOMEM; return 0; }
+ strcpy(d->path, path);
+ strcat(d->path, "/*.*");
+ d->hdl = -1;
+ return d;
+}
+
+struct dirent * readdir(DIR *d) {
+ static struct dirent de;
+ if (d->hdl < 0) {
+ d->hdl = _findfirsti64(d->path, &d->blk);
+ if (d->hdl < 0) {
+ if (errno == ENOENT) errno = 0;
+ return 0;
+ }
+ }
+ else {
+ int r = _findnexti64(d->hdl, &d->blk);
+ if (r < 0) {
+ if (errno == ENOENT) errno = 0;
+ return 0;
+ }
+ }
+ strcpy(de.d_name, d->blk.name);
+ de.d_size = d->blk.size;
+ de.d_atime = d->blk.time_access;
+ de.d_ctime = d->blk.time_create;
+ de.d_wtime = d->blk.time_write;
+ return &de;
+}
+
+int closedir(DIR * d) {
+ int r = 0;
+ if (!d) {
+ errno = EBADF;
+ return -1;
+ }
+ if (d->hdl >= 0) r = _findclose(d->hdl);
+ free(d);
+ return r;
+}
+
+char * canonicalize_file_name(const char * path) {
+ char buf[MAX_PATH];
+ char * basename;
+ int i = 0;
+ DWORD len = GetFullPathName(path, sizeof(buf), buf, &basename);
+ if (len == 0) {
+ errno = ENOENT;
+ return NULL;
+ }
+ if (len > MAX_PATH - 1) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ while (buf[i] != 0) {
+ if (buf[i] == '\\') buf[i] = '/';
+ i++;
+ }
+ return strdup(buf);
+}
+
+int getuid(void) {
+ /* Windows user is always a superuser :) */
+ return 0;
+}
+
+int geteuid(void) {
+ return 0;
+}
+
+int getgid(void) {
+ return 0;
+}
+
+int getegid(void) {
+ return 0;
+}
+
+char * get_os_name(void) {
+ static char str[256];
+ OSVERSIONINFOEX info;
+ memset(&info, 0, sizeof(info));
+ info.dwOSVersionInfoSize = sizeof(info);
+ GetVersionEx((OSVERSIONINFO *)&info);
+ switch (info.dwMajorVersion) {
+ case 4:
+ return "Windows NT";
+ case 5:
+ switch (info.dwMinorVersion) {
+ case 0: return "Windows 2000";
+ case 1: return "Windows XP";
+ case 2: return "Windows Server 2003";
+ }
+ break;
+ case 6:
+ return "Windows Vista";
+ }
+ snprintf(str, sizeof(str), "Windows %d.%d", info.dwMajorVersion, info.dwMajorVersion);
+ return str;
+}
+
+char * get_user_home(void) {
+ static char buf[MAX_PATH];
+ if (buf[0] != 0) return buf;
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, buf))) return buf;
+ return NULL;
+}
+
+void ini_mdep(void) {
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+ wVersionRequested = MAKEWORD( 1, 1 );
+ err = WSAStartup( wVersionRequested, &wsaData );
+ if ( err != 0 ) {
+ fprintf(stderr, "Couldn't access winsock.dll.\n");
+ exit(1);
+ }
+ /* Confirm that the Windows Sockets DLL supports 1.1.*/
+ /* Note that if the DLL supports versions greater */
+ /* than 1.1 in addition to 1.1, it will still return */
+ /* 1.1 in wVersion since that is the version we */
+ /* requested. */
+ if (LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1) {
+ fprintf(stderr, "Unacceptable version of winsock.dll.\n");
+ WSACleanup();
+ exit(1);
+ }
+}
+
+#elif defined(_WRS_KERNEL)
+
+void usleep(useconds_t useconds) {
+ struct timespec tv;
+ tv.tv_sec = useconds / 1000000;
+ tv.tv_nsec = (useconds % 1000000) * 1000;
+ nanosleep(&tv, NULL);
+}
+
+int truncate(char * path, int64 size) {
+ int f = open(path, O_RDWR, 0);
+ if (f < 0) return -1;
+ if (ftruncate(f, size) < 0) {
+ int err = errno;
+ close(f);
+ errno = err;
+ return -1;
+ }
+ return close(f);
+}
+
+char * canonicalize_file_name(const char * path) {
+ char buf[PATH_MAX];
+ int i = 0, j = 0;
+ if (path[0] == '.' && (path[1] == '/' || path[1] == '\\' || path[1] == 0)) {
+ getcwd(buf, sizeof(buf));
+ j = strlen(buf);
+ if (j == 1 && buf[0] == '/') j = 0;
+ i = 1;
+ }
+ else if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || path[2] == '\\' || path[2] == 0)) {
+ getcwd(buf, sizeof(buf));
+ j = strlen(buf);
+ while (j > 0 && buf[j - 1] != '/') j--;
+ if (j > 0 && buf[j - 1] == '/') j--;
+ i = 2;
+ }
+ while (path[i] && j < PATH_MAX - 1) {
+ char ch = path[i];
+ if (ch == '\\') ch = '/';
+ if (ch == '/') {
+ if (path[i + 1] == '/' || path[i + 1] == '\\') {
+ i++;
+ continue;
+ }
+ if (path[i + 1] == '.') {
+ if (path[i + 2] == 0) {
+ break;
+ }
+ if (path[i + 2] == '/' || path[i + 2] == '\\') {
+ i += 2;
+ continue;
+ }
+ if ((j == 0 || buf[0] == '/') && path[i + 2] == '.') {
+ if (path[i + 3] == '/' || path[i + 3] == '\\' || path[i + 3] == 0) {
+ while (j > 0 && buf[j - 1] != '/') j--;
+ if (j > 0 && buf[j - 1] == '/') j--;
+ i += 3;
+ continue;
+ }
+ }
+ }
+ }
+ buf[j++] = ch;
+ i++;
+ }
+ if (j == 0 && path[0] != 0) buf[j++] = '/';
+ buf[j] = 0;
+ return strdup(buf);
+}
+
+int getuid(void) {
+ return 0;
+}
+
+int geteuid(void) {
+ return 0;
+}
+
+int getgid(void) {
+ return 0;
+}
+
+int getegid(void) {
+ return 0;
+}
+
+char * get_os_name(void) {
+ static char str[256];
+ snprintf(str, sizeof(str), "VxWorks %s", kernelVersion());
+ return str;
+}
+
+char * get_user_home(void) {
+ return "/";
+}
+
+void ini_mdep(void) {
+ pthread_attr_init(&pthread_create_attr);
+ pthread_attr_setstacksize(&pthread_create_attr, 0x4000);
+ pthread_attr_setname(&pthread_create_attr, "tTcf");
+}
+
+#else
+
+#include <pwd.h>
+#include <sys/utsname.h>
+#include <asm/unistd.h>
+
+char * get_os_name(void) {
+ static char str[256];
+ struct utsname info;
+ memset(&info, 0, sizeof(info));
+ uname(&info);
+ assert(strlen(info.sysname) + strlen(info.release) < sizeof(str));
+ snprintf(str, sizeof(str), "%s %s", info.sysname, info.release);
+ return str;
+}
+
+char * get_user_home(void) {
+ static char buf[PATH_MAX];
+ if (buf[0] == 0) {
+ struct passwd * pwd = getpwuid(getuid());
+ if (pwd == NULL) return NULL;
+ strcpy(buf, pwd->pw_dir);
+ }
+ return buf;
+}
+
+int tkill(pid_t pid, int signal) {
+ return syscall(__NR_tkill, pid, signal);
+}
+
+void ini_mdep(void) {
+ pthread_attr_init(&pthread_create_attr);
+ pthread_attr_setstacksize(&pthread_create_attr, 0x8000);
+}
+
+#endif
+
diff --git a/mdep.h b/mdep.h
new file mode 100644
index 00000000..454ecf6b
--- /dev/null
+++ b/mdep.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * 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.
+ */
+
+#ifndef D_mdep
+#define D_mdep
+
+#ifdef WIN32
+/* MS Windows NT/XP */
+
+#define _WIN32_WINNT 0x0400
+#pragma warning(disable:4615)
+
+#include <windows.h>
+#include <winsock.h>
+#include <memory.h>
+#include <process.h>
+#include <IPHlpApi.h>
+#include <time.h>
+#include <io.h>
+
+#define FILE_PATH_SIZE MAX_PATH
+
+typedef int socklen_t;
+#ifdef __GNUC__
+#define _WIN32_IE 0x0500
+#else
+#define __i386__
+typedef unsigned long pid_t;
+#endif
+typedef unsigned long useconds_t;
+
+typedef struct {
+ unsigned long ebx, ecx, edx, esi, edi, ebp, eax;
+ unsigned short ds, __ds, es, __es;
+ unsigned short fs, __fs, gs, __gs;
+ unsigned long orig_eax, eip;
+ unsigned short cs, __cs;
+ long eflags, esp;
+ unsigned short ss, __ss;
+} REG_SET;
+
+#define get_regs_PC(x) x.eip
+#define set_regs_PC(x,y) x.eip = (unsigned long)(y)
+
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+
+#define SIGTRAP 5
+#define SIGKILL 9
+#define SIGSTOP 19
+
+#define ETIMEDOUT 100
+
+#define vsnprintf _vsnprintf
+
+#define CLOCK_REALTIME 1
+typedef int clockid_t;
+extern int clock_gettime(clockid_t clock_id, struct timespec * tp);
+
+extern void usleep(useconds_t useconds);
+
+/*
+ * PThreads emulation.
+ */
+typedef HANDLE pthread_t;
+typedef HANDLE pthread_mutex_t;
+typedef int pthread_attr_t;
+typedef struct {
+ int waiters_count;
+ CRITICAL_SECTION waiters_count_lock;
+ HANDLE sema;
+ HANDLE waiters_done;
+ size_t was_broadcast;
+} pthread_cond_t;
+
+extern void pthread_mutex_init(pthread_mutex_t * mutex, void * attr);
+extern void pthread_cond_init(pthread_cond_t * cond, void * attr);
+
+extern void pthread_cond_signal(pthread_cond_t * cond);
+extern void pthread_cond_broadcast(pthread_cond_t *cond);
+extern int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);
+extern int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
+ struct timespec * timeout);
+extern void pthread_mutex_lock(pthread_mutex_t * mutex);
+extern void pthread_mutex_unlock(pthread_mutex_t * mutex);
+extern pthread_t pthread_self(void);
+extern int pthread_create(pthread_t * thread, pthread_attr_t * attr,
+ void * (*start_routine)(void *), void * arg);
+extern int pthread_join(pthread_t thread, void **value_ptr);
+
+/*
+ * Windows socket functions don't set errno as expected.
+ * Wrappers are provided to workaround the problem.
+ * TODO: more socket function wrappers are needed for better error reports on Windows
+ */
+#define socket(af, type, protocol) wsa_socket(af, type, protocol)
+#define bind(socket, addr, addr_size) wsa_bind(socket, addr, addr_size)
+extern int wsa_bind(int socket, const struct sockaddr * addr, int addr_size);
+extern int wsa_socket(int af, int type, int protocol);
+
+typedef __int64 int64;
+typedef unsigned __int64 uns64;
+#define lseek _lseeki64
+typedef struct _stati64 struct_stat;
+#define stat _stati64
+#define lstat _stati64
+#define fstat _fstati64
+extern int truncate(const char * path, int64 size);
+extern int ftruncate(int f, int64 size);
+#define utimbuf _utimbuf
+#define utime _utime
+#define futime _futime
+#define snprintf _snprintf
+
+struct DIR {
+ long hdl;
+ struct _finddatai64_t blk;
+ char path[FILE_PATH_SIZE];
+};
+
+struct dirent {
+ char d_name[FILE_PATH_SIZE];
+ int64 d_size;
+ time_t d_atime;
+ time_t d_ctime;
+ time_t d_wtime;
+};
+
+typedef struct DIR DIR;
+
+extern DIR * opendir(const char * path);
+extern int closedir(DIR * dir);
+extern struct dirent * readdir(DIR * dir);
+
+extern char * canonicalize_file_name(const char * path);
+
+#define O_LARGEFILE 0
+
+extern int getuid(void);
+extern int geteuid(void);
+extern int getgid(void);
+extern int getegid(void);
+
+#elif defined(_WRS_KERNEL)
+/* VxWork kernel module */
+
+#define INET
+
+#include <vxWorks.h>
+#include <regs.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <wrn/coreip/sockLib.h>
+#include <wrn/coreip/hostLib.h>
+
+#define environ taskIdCurrent->ppEnviron
+
+#define get_regs_PC(x) (*(int *)((int)&(x) + PC_OFFSET))
+#define set_regs_PC(x,y) *(int *)((int)&(x) + PC_OFFSET) = (int)(y)
+
+#define closesocket close
+
+typedef long long int64;
+typedef unsigned long long uns64;
+typedef unsigned long useconds_t;
+
+#define FILE_PATH_SIZE PATH_MAX
+#define O_BINARY 0
+#define O_LARGEFILE 0
+#define lstat stat
+typedef struct stat struct_stat;
+#define ifr_netmask ifr_addr
+#define SA_LEN(addr) ((addr)->sa_len)
+
+extern int truncate(char * path, int64 size);
+extern char * canonicalize_file_name(const char * path);
+
+extern void usleep(useconds_t useconds);
+
+extern int getuid(void);
+extern int geteuid(void);
+extern int getgid(void);
+extern int getegid(void);
+
+#else
+/* Linux or UNIX */
+
+#ifndef _LARGEFILE_SOURCE
+#error "Need CC command line option: -D_LARGEFILE_SOURCE"
+#endif
+
+#ifndef _GNU_SOURCE
+#error "Need CC command line option: -D_GNU_SOURCE"
+#endif
+
+#include <unistd.h>
+#include <memory.h>
+#include <pthread.h>
+#include <netdb.h>
+#include <sys/user.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#define FILE_PATH_SIZE PATH_MAX
+
+#define closesocket close
+
+typedef __int64_t int64;
+typedef __uint64_t uns64;
+typedef struct user_regs_struct REG_SET;
+typedef struct stat struct_stat;
+#define O_BINARY 0
+
+#define get_regs_PC(x) x.eip
+#define set_regs_PC(x,y) x.eip = (unsigned long)(y)
+
+#ifndef SA_LEN
+# ifdef HAVE_SOCKADDR_SA_LEN
+# define SA_LEN(addr) ((addr)->sa_len)
+# else /* HAVE_SOCKADDR_SA_LEN */
+# ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+static size_t get_sa_len(const struct sockaddr *addr) {
+ switch (addr->sa_family) {
+# ifdef AF_UNIX
+ case AF_UNIX: return sizeof(struct sockaddr_un);
+# endif
+# ifdef AF_INET
+ case AF_INET: return (sizeof (struct sockaddr_in));
+# endif
+# ifdef AF_INET6
+ case AF_INET6: return (sizeof (struct sockaddr_in6));
+# endif
+ default: return (sizeof (struct sockaddr));
+ }
+}
+# define SA_LEN(addr) (get_sa_len(addr))
+# else /* HAVE_SOCKADDR_STORAGE */
+# define SA_LEN(addr) (sizeof (struct sockaddr))
+# endif /* HAVE_SOCKADDR_STORAGE */
+# endif /* HAVE_SOCKADDR_SA_LEN */
+#endif /* SA_LEN */
+
+extern int tkill(pid_t pid, int signal);
+
+#endif
+
+extern pthread_attr_t pthread_create_attr;
+
+extern char * get_os_name(void);
+extern char * get_user_home(void);
+
+extern void ini_mdep(void);
+
+#endif
diff --git a/memory.c b/memory.c
new file mode 100644
index 00000000..44d8a433
--- /dev/null
+++ b/memory.c
@@ -0,0 +1,579 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF Memory - memory access service.
+ */
+
+#include "config.h"
+#if SERVICE_Memory
+
+#include <assert.h>
+#include "mdep.h"
+#include "protocol.h"
+#include "context.h"
+#include "json.h"
+#include "exceptions.h"
+#include "runctrl.h"
+#include "myalloc.h"
+#include "channel.h"
+#include "base64.h"
+
+static const char * MEMORY = "Memory";
+
+#define BYTE_VALID 0x00
+#define BYTE_UNKNOWN 0x01
+#define BYTE_INVALID 0x02
+#define BYTE_CANNOT_READ 0x04
+#define BYTE_CANNOT_WRITE 0x08
+
+#define CMD_GET 1
+#define CMD_SET 2
+#define CMD_FILL 3
+
+#define BUF_SIZE 0x1000
+
+struct MemoryCommandArgs {
+ InputStream * inp;
+ OutputStream * out;
+ char token[256];
+ unsigned long addr;
+ unsigned long size;
+ int word_size;
+ int mode;
+ Context * ctx;
+};
+
+static void write_context(OutputStream * out, Context * ctx) {
+ assert(!ctx->exited);
+
+ out->write(out, '{');
+
+ json_write_string(out, "ID");
+ out->write(out, ':');
+ json_write_string(out, container_id(ctx));
+
+#if !defined(_WRS_KERNEL)
+ out->write(out, ',');
+ json_write_string(out, "ProcessID");
+ out->write(out, ':');
+ json_write_string(out, pid2id(ctx->mem, 0));
+#endif
+
+ /* Check endianness */
+ {
+ short n = 0x0201;
+ char * p = (char *)&n;
+ out->write(out, ',');
+ json_write_string(out, "BigEndian");
+ out->write(out, ':');
+ json_write_boolean(out, *p == 0x02);
+ }
+
+ out->write(out, ',');
+ json_write_string(out, "AddressSize");
+ out->write(out, ':');
+ json_write_ulong(out, sizeof(char *));
+
+ out->write(out, '}');
+}
+
+static void write_ranges(OutputStream * out, unsigned long addr, int size, int offs, int status, char * msg) {
+ out->write(out, '[');
+ if (offs > 0) {
+ out->write(out, '{');
+
+ json_write_string(out, "addr");
+ out->write(out, ':');
+ json_write_ulong(out, addr);
+ out->write(out, ',');
+
+ json_write_string(out, "size");
+ out->write(out, ':');
+ json_write_ulong(out, offs);
+ out->write(out, ',');
+
+ json_write_string(out, "stat");
+ out->write(out, ':');
+ json_write_ulong(out, 0);
+
+ out->write(out, '}');
+ out->write(out, ',');
+ }
+ if (offs < size) {
+ out->write(out, '{');
+
+ json_write_string(out, "addr");
+ out->write(out, ':');
+ json_write_ulong(out, addr + offs);
+ out->write(out, ',');
+
+ json_write_string(out, "size");
+ out->write(out, ':');
+ json_write_ulong(out, size - offs);
+ out->write(out, ',');
+
+ json_write_string(out, "stat");
+ out->write(out, ':');
+ json_write_ulong(out, status);
+ out->write(out, ',');
+
+ json_write_string(out, "msg");
+ out->write(out, ':');
+ json_write_string(out, msg);
+
+ out->write(out, '}');
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+}
+
+static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ Context * ctx = NULL;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ ctx = id2ctx(id);
+
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ if (err == 0) {
+ write_context(out, ctx);
+ }
+ else {
+ write_stringz(out, "null");
+ }
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ write_errno(out, 0);
+
+ out->write(out, '[');
+ if (id[0] == 0) {
+ LINK * qp;
+ int cnt = 0;
+ for (qp = context_root.next; qp != &context_root; qp = qp->next) {
+ Context * ctx = ctxl2ctxp(qp);
+ if (ctx->exited) continue;
+ if (ctx->parent != NULL) continue;
+ if (cnt > 0) out->write(out, ',');
+ json_write_string(out, container_id(ctx));
+ cnt++;
+ }
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static struct MemoryCommandArgs * read_command_args(char * token, InputStream * inp, OutputStream * out, int cmd) {
+ int err = 0;
+ char id[256];
+ struct MemoryCommandArgs buf;
+ memset(&buf, 0, sizeof(buf));
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf.addr = json_read_ulong(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf.word_size = (int)json_read_long(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf.size = (int)json_read_long(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf.mode = (int)json_read_long(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (cmd == CMD_GET && inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ buf.ctx = id2ctx(id);
+ if (buf.ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (buf.ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (err != 0) {
+ if (cmd == CMD_SET || cmd == CMD_FILL) {
+ if (inp->read(inp) != '[') exception(ERR_JSON_SYNTAX);
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ char ch;
+ json_read_ulong(inp);
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ }
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_stringz(out, "null");
+ write_errno(out, err);
+ write_stringz(out, "null");
+ out->write(out, MARKER_EOM);
+ return NULL;
+ }
+ else {
+ struct MemoryCommandArgs * args = (struct MemoryCommandArgs *)
+ loc_alloc(sizeof(struct MemoryCommandArgs));
+ *args = buf;
+ args->inp = inp;
+ args->out = out;
+ strncpy(args->token, token, sizeof(args->token));
+ stream_lock(out);
+ context_lock(buf.ctx);
+ return args;
+ }
+}
+
+static void send_event_memory_changed(OutputStream * out, Context * ctx, unsigned long addr, unsigned long size) {
+ write_stringz(out, "E");
+ write_stringz(out, MEMORY);
+ write_stringz(out, "memoryChanged");
+
+ json_write_string(out, container_id(ctx));
+ out->write(out, 0);
+
+ /* <array of addres ranges> */
+ out->write(out, '[');
+ out->write(out, '{');
+
+ json_write_string(out, "addr");
+ out->write(out, ':');
+ json_write_ulong(out, addr);
+
+ out->write(out, ',');
+
+ json_write_string(out, "size");
+ out->write(out, ':');
+ json_write_ulong(out, size);
+
+ out->write(out, '}');
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void safe_memory_set(void * parm) {
+ struct MemoryCommandArgs * args = (struct MemoryCommandArgs *)parm;
+ InputStream * inp = args->inp;
+ OutputStream * out = args->out;
+ char * token = args->token;
+ unsigned long addr0 = args->addr;
+ unsigned long addr = args->addr;
+ unsigned long size = 0;
+ int word_size = args->word_size;
+ int mode = args->mode;
+ Context * ctx = args->ctx;
+ char buf[BUF_SIZE];
+ int err = 0;
+
+ if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (inp->read(inp) != '"') exception(ERR_JSON_SYNTAX);
+
+ for (;;) {
+ int rd = read_base64(inp, buf, sizeof(buf));
+ if (rd == 0) break;
+ if (err == 0) {
+ // TODO: word size, mode
+ if (context_write_mem(ctx, addr, buf, rd) < 0) {
+ err = errno;
+ }
+ else {
+ addr += rd;
+ }
+ }
+ size += rd;
+ }
+
+ if (inp->read(inp) != '"') exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ send_event_memory_changed(&broadcast_stream, ctx, addr0, size);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ if (err == 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ char msg[0x400];
+ strncpy(msg, errno_to_str(err), sizeof(msg));
+ write_ranges(out, addr0, size, addr - addr0, BYTE_INVALID | BYTE_CANNOT_WRITE, msg);
+ }
+ out->write(out, MARKER_EOM);
+ out->flush(out);
+ stream_unlock(out);
+ context_unlock(ctx);
+ loc_free(args);
+}
+
+static void command_set(char * token, InputStream * inp, OutputStream * out) {
+ struct MemoryCommandArgs * args = read_command_args(token, inp, out, CMD_SET);
+ if (args != NULL) post_safe_event(safe_memory_set, args);
+}
+
+static void safe_memory_get(void * parm) {
+ struct MemoryCommandArgs * args = (struct MemoryCommandArgs *)parm;
+ OutputStream * out = args->out;
+ char * token = args->token;
+ unsigned long addr0 = args->addr;
+ unsigned long addr = args->addr;
+ unsigned long size = args->size;
+ int word_size = args->word_size;
+ int mode = args->mode;
+ Context * ctx = args->ctx;
+ char buf[BUF_SIZE + 4];
+ int buf_pos = 0;
+ int err = 0;
+ int rem = 0;
+ unsigned long chk = 0;
+
+ if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ out->write(out, '"');
+ while (err == 0 && addr < addr0 + size) {
+ int rd = addr0 + size - addr;
+ if (rd > BUF_SIZE) rd = BUF_SIZE;
+ // TODO: word size, mode
+ if (context_read_mem(ctx, addr, buf + rem, rd) < 0) {
+ err = errno;
+ }
+ else {
+ int i = 0, j = rem + rd;
+ rem = j % 3;
+ chk += write_base64(out, buf, j - rem);
+ for (i = 0; i < rem; i++) buf[i] = buf[j - rem + i];
+ addr += rd;
+ }
+ }
+ chk += write_base64(out, buf, rem);
+ out->write(out, '"');
+ out->write(out, 0);
+ assert(chk == ((addr - addr0) + 2) / 3 * 4);
+
+ write_errno(out, err);
+ if (err == 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ char msg[0x400];
+ strncpy(msg, errno_to_str(err), sizeof(msg));
+ write_ranges(out, addr0, size, addr - addr0, BYTE_INVALID | BYTE_CANNOT_READ, msg);
+ }
+ out->write(out, MARKER_EOM);
+ out->flush(out);
+ stream_unlock(out);
+ context_unlock(ctx);
+ loc_free(args);
+}
+
+static void command_get(char * token, InputStream * inp, OutputStream * out) {
+ struct MemoryCommandArgs * args = read_command_args(token, inp, out, CMD_GET);
+ if (args != NULL) post_safe_event(safe_memory_get, args);
+}
+
+static void safe_memory_fill(void * parm) {
+ struct MemoryCommandArgs * args = (struct MemoryCommandArgs *)parm;
+ InputStream * inp = args->inp;
+ OutputStream * out = args->out;
+ char * token = args->token;
+ unsigned long addr0 = args->addr;
+ unsigned long addr = args->addr;
+ unsigned long size = args->size;
+ int word_size = args->word_size;
+ int mode = args->mode;
+ Context * ctx = args->ctx;
+ char buf[0x1000];
+ int buf_pos = 0;
+ int err = 0;
+
+ if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (inp->read(inp) != '[') exception(ERR_JSON_SYNTAX);
+ if (inp->peek(inp) == ']') {
+ inp->read(inp);
+ }
+ else {
+ while (1) {
+ char ch;
+ if (err == 0) {
+ if (buf_pos >= sizeof(buf)) err = ERR_BUFFER_OVERFLOW;
+ else buf[buf_pos++] = (char)json_read_ulong(inp);
+ }
+ else {
+ json_read_ulong(inp);
+ }
+ ch = inp->read(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ while (err == 0 && buf_pos < (int)size && buf_pos <= sizeof(buf) / 2) {
+ if (buf_pos == 0) {
+ buf[buf_pos++] = 0;
+ }
+ else {
+ memcpy(buf + buf_pos, buf, buf_pos);
+ buf_pos *= 2;
+ }
+ }
+
+ while (err == 0 && addr < addr0 + size) {
+ int wr = addr0 + size - addr;
+ if (wr > buf_pos) wr = buf_pos;
+ // TODO: word size, mode
+ if (context_write_mem(ctx, addr, buf, wr) < 0) {
+ err = errno;
+ }
+ else {
+ addr += wr;
+ }
+ }
+
+ send_event_memory_changed(&broadcast_stream, ctx, addr0, size);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ if (err == 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ char msg[0x400];
+ strncpy(msg, errno_to_str(err), sizeof(msg));
+ write_ranges(out, addr0, size, addr - addr0, BYTE_INVALID | BYTE_CANNOT_WRITE, msg);
+ }
+ out->write(out, MARKER_EOM);
+ out->flush(out);
+ stream_unlock(out);
+ context_unlock(ctx);
+ loc_free(args);
+}
+
+static void command_fill(char * token, InputStream * inp, OutputStream * out) {
+ struct MemoryCommandArgs * args = read_command_args(token, inp, out, CMD_FILL);
+ if (args != NULL) post_safe_event(safe_memory_fill, args);
+}
+
+static void send_event_context_added(OutputStream * out, Context * ctx) {
+ write_stringz(out, "E");
+ write_stringz(out, MEMORY);
+ write_stringz(out, "contextAdded");
+
+ /* <array of context data> */
+ out->write(out, '[');
+ write_context(out, ctx);
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_context_changed(OutputStream * out, Context * ctx) {
+ write_stringz(out, "E");
+ write_stringz(out, MEMORY);
+ write_stringz(out, "contextChanged");
+
+ /* <array of context data> */
+ out->write(out, '[');
+ write_context(out, ctx);
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_context_removed(OutputStream * out, Context * ctx) {
+ write_stringz(out, "E");
+ write_stringz(out, MEMORY);
+ write_stringz(out, "contextRemoved");
+
+ /* <array of context IDs> */
+ out->write(out, '[');
+ json_write_string(out, container_id(ctx));
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void event_context_created(Context * ctx) {
+ if (ctx->parent != NULL) return;
+ send_event_context_added(&broadcast_stream, ctx);
+ broadcast_stream.flush(&broadcast_stream);
+}
+
+static void event_context_changed(Context * ctx) {
+ if (ctx->parent != NULL) return;
+ send_event_context_changed(&broadcast_stream, ctx);
+ broadcast_stream.flush(&broadcast_stream);
+}
+
+static void event_context_exited(Context * ctx) {
+ if (ctx->parent != NULL) return;
+ send_event_context_removed(&broadcast_stream, ctx);
+ broadcast_stream.flush(&broadcast_stream);
+}
+
+void ini_memory_service(void) {
+ static ContextEventListener listener = {
+ event_context_created,
+ event_context_exited,
+ NULL,
+ NULL,
+ event_context_changed,
+ NULL
+ };
+ add_context_event_listener(&listener);
+ add_command_handler(MEMORY, "getContext", command_get_context);
+ add_command_handler(MEMORY, "getChildren", command_get_children);
+ add_command_handler(MEMORY, "set", command_set);
+ add_command_handler(MEMORY, "get", command_get);
+ add_command_handler(MEMORY, "fill", command_fill);
+}
+
+#endif
+
diff --git a/memory.h b/memory.h
new file mode 100644
index 00000000..3087c4ea
--- /dev/null
+++ b/memory.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * TCF Memory - memory access service.
+ */
+
+#ifndef D_memory
+#define D_memory
+
+/*
+ * Initialize memory service.
+ */
+extern void ini_memory_service(void);
+
+
+#endif
diff --git a/myalloc.c b/myalloc.c
new file mode 100644
index 00000000..6abcac9e
--- /dev/null
+++ b/myalloc.c
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Local memory heap manager.
+ */
+
+#include <string.h>
+#include "trace.h"
+#include "myalloc.h"
+
+void * loc_alloc(size_t size) {
+ void * p;
+
+ if (size == 0) {
+ size = 1;
+ }
+ if ((p = malloc(size)) == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+ trace(LOG_ALLOC, "loc_alloc(%d) = %#x", size, p);
+ return p;
+}
+
+void * loc_alloc_zero(size_t size) {
+ void * p;
+
+ if (size == 0) {
+ size = 1;
+ }
+ if ((p = malloc(size)) == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+ memset(p, 0, size);
+ trace(LOG_ALLOC, "loc_alloc_zero(%d) = %#x", size, p);
+ return p;
+}
+
+void * loc_realloc(void * ptr, size_t size) {
+ void * p;
+
+ if (size == 0) {
+ size = 1;
+ }
+ if ((p = realloc(ptr, size)) == NULL) {
+ perror("realloc");
+ exit(1);
+ }
+ trace(LOG_ALLOC, "loc_realloc(%#x, %d) = %#x", ptr, size, p);
+ return p;
+}
+
+void loc_free(void *p) {
+ trace(LOG_ALLOC, "loc_free %#x", p);
+ free(p);
+}
+
diff --git a/myalloc.h b/myalloc.h
new file mode 100644
index 00000000..1f3cc595
--- /dev/null
+++ b/myalloc.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Local memory heap manager.
+ */
+
+#ifndef D_myalloc
+#define D_myalloc
+
+#include <stdlib.h>
+
+void * loc_alloc(size_t size);
+void * loc_alloc_zero(size_t size);
+void * loc_realloc(void *ptr, size_t size);
+void loc_free(void *p);
+
+#endif
diff --git a/processes.c b/processes.c
new file mode 100644
index 00000000..a66d41f8
--- /dev/null
+++ b/processes.c
@@ -0,0 +1,419 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF Processes - process control service.
+ * 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.
+ */
+
+#include "config.h"
+#if SERVICE_Processes
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <assert.h>
+#include "mdep.h"
+#include "myalloc.h"
+#include "protocol.h"
+#include "context.h"
+#include "json.h"
+#include "exceptions.h"
+
+static const char * PROCESSES = "Processes";
+
+#if defined(WIN32)
+# include <direct.h>
+#elif defined(_WRS_KERNEL)
+# include <symLib.h>
+# include <sysSymTbl.h>
+#else
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <unistd.h>
+# include <dirent.h>
+#endif
+
+static void write_context(OutputStream * out, char * id, char * dir) {
+ Context * ctx = NULL;
+
+ out->write(out, '{');
+
+#if defined(WIN32)
+#elif defined(_WRS_KERNEL)
+#else
+ if (chdir(dir) >= 0) {
+ int sz;
+ char fnm[FILE_PATH_SIZE + 1];
+
+ json_write_string(out, "CanTerminate");
+ out->write(out, ':');
+ json_write_boolean(out, 1);
+ out->write(out, ',');
+
+ if ((sz = readlink("exe", fnm, FILE_PATH_SIZE)) > 0) {
+ fnm[sz] = 0;
+ json_write_string(out, "Name");
+ out->write(out, ':');
+ json_write_string(out, fnm);
+ out->write(out, ',');
+ }
+ }
+#endif
+
+ ctx = id2ctx(id);
+ if (ctx != NULL) {
+ json_write_string(out, "Attached");
+ out->write(out, ':');
+ json_write_boolean(out, 1);
+ out->write(out, ',');
+ }
+
+ json_write_string(out, "ID");
+ out->write(out, ':');
+ json_write_string(out, id);
+
+ out->write(out, '}');
+}
+
+static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ pid_t pid, parent;
+ char dir[FILE_PATH_SIZE];
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ pid = id2pid(id, &parent);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ pid = id2pid(id, &parent);
+ snprintf(dir, sizeof(dir), "/proc/%d", pid);
+ if (pid != 0 && parent == 0) {
+#if defined(WIN32)
+#elif defined(_WRS_KERNEL)
+ if (TASK_ID_VERIFY(pid) == ERROR) err = ERR_INV_CONTEXT;
+#else
+ struct_stat st;
+ if (lstat(dir, &st) < 0) err = errno;
+ else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
+#endif
+ }
+
+ write_errno(out, err);
+
+ if (err == 0 && pid != 0 && parent == 0) {
+ write_context(out, id, dir);
+ out->write(out, 0);
+ }
+ else {
+ write_stringz(out, "null");
+ }
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ pid_t parent = 0;
+ int attached_only;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ attached_only = json_read_boolean(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ if (id[0] != 0) {
+ write_errno(out, 0);
+ write_stringz(out, "null");
+ }
+ else {
+#if defined(WIN32)
+#elif defined(_WRS_KERNEL)
+ int i = 0;
+ int cnt = 0;
+ int ids_cnt = 0;
+ int ids_max = 500;
+ int * ids = (int *)loc_alloc(ids_max * sizeof(int));
+ while (1) {
+ ids_cnt = taskIdListGet(ids, ids_max);
+ if (ids_cnt < ids_max) break;
+ loc_free(ids);
+ ids_max *= 2;
+ ids = (int *)loc_alloc(ids_max * sizeof(int));
+ }
+ out->write(out, '[');
+ for (i = 0; i < ids_cnt; i++) {
+ if (!attached_only || context_find_from_pid(ids[i]) != NULL) {
+ if (cnt > 0) out->write(out, ',');
+ json_write_string(out, pid2id(ids[i], 0));
+ cnt++;
+ }
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+#else
+ DIR * proc = opendir("/proc");
+ if (proc == NULL) {
+ write_errno(out, errno);
+ write_stringz(out, "null");
+ }
+ else {
+ int cnt = 0;
+ write_errno(out, 0);
+ out->write(out, '[');
+ for (;;) {
+ struct dirent * ent = readdir(proc);
+ if (ent == NULL) break;
+ if (ent->d_name[0] >= '1' && ent->d_name[0] <= '9') {
+ pid_t pid = atol(ent->d_name);
+ if (!attached_only || context_find_from_pid(pid) != NULL) {
+ if (cnt > 0) out->write(out, ',');
+ json_write_string(out, pid2id(pid, 0));
+ cnt++;
+ }
+ }
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+ closedir(proc);
+ }
+#endif
+ }
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_attach(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ pid_t pid, parent;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ pid = id2pid(id, &parent);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ if (parent != 0) {
+ err = ERR_INV_CONTEXT;
+ }
+ else if (context_find_from_pid(pid) != NULL) {
+ err = ERR_ALREADY_ATTACHED;
+ }
+ else {
+ if (context_attach(pid, NULL) < 0) err = errno;
+ }
+
+ write_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_detach(char * token, InputStream * inp, OutputStream * out) {
+ // TODO: implement command_detach()
+ exception(ERR_PROTOCOL);
+}
+
+static void command_terminate(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ pid_t pid, parent;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ pid = id2pid(id, &parent);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ if (parent != 0) {
+ err = ERR_INV_CONTEXT;
+ }
+ else {
+#if defined(WIN32)
+ err = ENOSYS;
+#elif defined(_WRS_KERNEL)
+ if (kill(pid, SIGTERM) < 0) err = errno;
+#else
+ if (kill(pid, SIGTERM) < 0) err = errno;
+#endif
+ }
+
+ write_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_signal(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ int signal = 0;
+ pid_t pid, parent;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ signal = (int)json_read_long(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ pid = id2pid(id, &parent);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ if (parent != 0) {
+ err = ERR_INV_CONTEXT;
+ }
+ else {
+#if defined(WIN32)
+ err = ENOSYS;
+#elif defined(_WRS_KERNEL)
+ if (kill(pid, signal) < 0) err = errno;
+#else
+ if (kill(pid, signal) < 0) err = errno;
+#endif
+ }
+
+ write_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_environment(char * token, InputStream * inp, OutputStream * out) {
+ char ** p = environ;
+
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, '[');
+ if (p != NULL) {
+ while (*p != NULL) {
+ if (p != environ) out->write(out, ',');
+ json_write_string(out, *p++);
+ }
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_start(char * token, InputStream * inp, OutputStream * out) {
+ Context * ctx = NULL;
+ int pid = 0;
+ int err = 0;
+ char dir[FILE_PATH_SIZE];
+ char exe[FILE_PATH_SIZE];
+ char ** args = NULL;
+ char ** envp = NULL;
+ int args_len = 0;
+ int envp_len = 0;
+ int attach = 0;
+ Trap trap;
+
+ if (set_trap(&trap)) {
+ json_read_string(inp, dir, sizeof(dir));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ json_read_string(inp, exe, sizeof(exe));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ args = json_read_alloc_string_array(inp, &args_len);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ envp = json_read_alloc_string_array(inp, &envp_len);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ attach = json_read_boolean(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (dir[0] != 0 && chdir(dir) < 0) err = errno;
+ if (err == 0) {
+#if defined(WIN32)
+#elif defined(_WRS_KERNEL)
+ char * ptr;
+ SYM_TYPE type;
+ if (symFindByName(sysSymTbl, exe, &ptr, &type) != OK) {
+ err = errno;
+ if (err == S_symLib_SYMBOL_NOT_FOUND) err = ERR_SYM_NOT_FOUND;
+ assert(err != 0);
+ }
+ else {
+ // TODO: arguments, environment
+ pid = taskCreate("tTcf", 100, 0, 0x4000, (FUNCPTR)ptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ if (attach) {
+ taskStop(pid);
+ taskActivate(pid);
+ assert(taskIsStopped(pid));
+ }
+ else {
+ taskActivate(pid);
+ }
+ }
+#else
+ pid = fork();
+ if (pid < 0) err = errno;
+ if (pid == 0) {
+ if (attach) tkill(getpid(), SIGSTOP);
+ exit(execve(exe, args, envp));
+ }
+#endif
+ }
+ if (attach) {
+ if (err == 0 && context_attach(pid, &ctx) < 0) err = errno;
+ if (ctx != NULL && !ctx->stopped) ctx->pending_intercept = 1;
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ if (err || pid == 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ char bf[256];
+ snprintf(dir, sizeof(dir), "/proc/%d", pid);
+ write_context(out, strcpy(bf, pid2id(pid, 0)), dir);
+ out->write(out, 0);
+ }
+ out->write(out, MARKER_EOM);
+ clear_trap(&trap);
+ }
+
+ loc_free(args);
+ loc_free(envp);
+
+ if (trap.error) exception(trap.error);
+}
+
+void ini_processes_service(void) {
+ add_command_handler(PROCESSES, "getContext", command_get_context);
+ add_command_handler(PROCESSES, "getChildren", command_get_children);
+ add_command_handler(PROCESSES, "attach", command_attach);
+ add_command_handler(PROCESSES, "detach", command_detach);
+ add_command_handler(PROCESSES, "terminate", command_terminate);
+ add_command_handler(PROCESSES, "signal", command_signal);
+ add_command_handler(PROCESSES, "getEnvironment", command_get_environment);
+ add_command_handler(PROCESSES, "start", command_start);
+}
+
+#endif
+
diff --git a/processes.h b/processes.h
new file mode 100644
index 00000000..96e2e925
--- /dev/null
+++ b/processes.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * TCF Processes - process control service.
+ * 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.
+ */
+
+#ifndef D_processes
+#define D_processes
+
+/*
+ * Initialize process control service.
+ */
+extern void ini_processes_service(void);
+
+
+#endif
diff --git a/protocol.c b/protocol.c
new file mode 100644
index 00000000..e9895fb3
--- /dev/null
+++ b/protocol.c
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF communication protocol.
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "protocol.h"
+#include "trace.h"
+#include "exceptions.h"
+#include "json.h"
+#include "myalloc.h"
+
+static const char * LOCATOR = "Locator";
+
+struct ServiceInfo {
+ const char * name;
+ struct ServiceInfo * next;
+};
+
+typedef struct ServiceInfo ServiceInfo;
+
+struct MessageHandlerInfo {
+ char type;
+ ServiceInfo * service;
+ const char * name;
+ MessageHandler handler;
+ struct MessageHandlerInfo * next;
+};
+
+typedef struct MessageHandlerInfo MessageHandlerInfo;
+
+#define MESSAGE_HASH_SIZE 127
+static MessageHandlerInfo * message_handlers[MESSAGE_HASH_SIZE];
+static ServiceInfo * services = NULL;
+
+int congestion_level = 0;
+
+static void read_stringz(InputStream * inp, char * str, size_t size) {
+ unsigned len = 0;
+ for (;;) {
+ int ch = inp->read(inp);
+ if (ch <= 0) break;
+ if (len < size - 1) str[len] = ch;
+ len++;
+ }
+ str[len] = 0;
+}
+
+static int message_hash(const char * service, const char * name) {
+ int i;
+ int h = 0;
+ for (i = 0; service[i]; i++) h += service[i];
+ for (i = 0; name[i]; i++) h += name[i];
+ h = h + h / MESSAGE_HASH_SIZE;
+ return h % MESSAGE_HASH_SIZE;
+}
+
+static MessageHandlerInfo * find_message_handler(char type, char * service, char * name) {
+ MessageHandlerInfo * c = message_handlers[message_hash(service, name)];
+ while (c != NULL) {
+ if (c->type == type && !strcmp(c->service->name, service) && !strcmp(c->name, name)) return c;
+ c = c->next;
+ }
+ trace(LOG_ALWAYS, "Unsupported TCF message: %c %s %s ...", type, service, name);
+ exception(ERR_PROTOCOL);
+ return NULL;
+}
+
+void handle_protocol_message(InputStream * inp, OutputStream * out) {
+ char type[8];
+ char token[256];
+ char service[256];
+ char name[256];
+ read_stringz(inp, type, sizeof(type));
+ if (strlen(type) != 1) {
+ trace(LOG_ALWAYS, "Invalid TCF message: %s ...", type);
+ exception(ERR_PROTOCOL);
+ }
+ else if (type[0] == 'C') {
+ Trap trap;
+ MessageHandlerInfo * c;
+ read_stringz(inp, token, sizeof(token));
+ read_stringz(inp, service, sizeof(service));
+ read_stringz(inp, name, sizeof(name));
+ trace(LOG_PROTOCOL, "Command: C %s %s %s ...", token, service, name);
+ c = find_message_handler('C', service, name);
+ if (set_trap(&trap)) {
+ c->handler(token, inp, out);
+ clear_trap(&trap);
+ }
+ else {
+ trace(LOG_ALWAYS, "Exception handling command %s.%s: %d %s",
+ service, name, trap.error, errno_to_str(trap.error));
+ exception(trap.error);
+ }
+ }
+ else if (type[0] == 'E') {
+ Trap trap;
+ MessageHandlerInfo * c;
+ read_stringz(inp, service, sizeof(service));
+ read_stringz(inp, name, sizeof(name));
+ trace(LOG_PROTOCOL, "Event: E %s %s ...", service, name);
+ c = find_message_handler('E', service, name);
+ if (set_trap(&trap)) {
+ c->handler(NULL, inp, out);
+ clear_trap(&trap);
+ }
+ else {
+ trace(LOG_ALWAYS, "Exception handling event %s.%s: %d %s",
+ service, name, trap.error, errno_to_str(trap.error));
+ exception(trap.error);
+ }
+ }
+ else if (type[0] == 'F') {
+ int n = 0;
+ int s = 0;
+ int ch = inp->read(inp);
+ if (ch == '-') {
+ s = 1;
+ ch = inp->read(inp);
+ }
+ while (ch >= '0' && ch <= '9') {
+ n = n * 10 + (ch - '0');
+ ch = inp->read(inp);
+ }
+ if (ch != MARKER_EOM) exception(ERR_PROTOCOL);
+ congestion_level = n;
+ }
+ else {
+ trace(LOG_ALWAYS, "Invalid TCF message: %s ...", type);
+ exception(ERR_PROTOCOL);
+ }
+}
+
+static void add_message_handler(const char type, const char * service, const char * name, MessageHandler handler) {
+ int h = message_hash(service, name);
+ ServiceInfo * s = services;
+ MessageHandlerInfo * c = (MessageHandlerInfo *)loc_alloc(sizeof(MessageHandlerInfo));
+ while (s != NULL && strcmp(s->name, service) != 0) s = s->next;
+ if (s == NULL) {
+ s = (ServiceInfo *)loc_alloc(sizeof(ServiceInfo));
+ s->name = service;
+ s->next = services;
+ services = s;
+ }
+ c->type = type;
+ c->service = s;
+ c->name = name;
+ c->handler = handler;
+ c->next = message_handlers[h];
+ message_handlers[h] = c;
+}
+
+void add_command_handler(const char * service, const char * name, MessageHandler handler) {
+ add_message_handler('C', service, name, handler);
+}
+
+void add_event_handler(const char * service, const char * name, MessageHandler handler) {
+ add_message_handler('E', service, name, handler);
+}
+
+void send_hello_message(OutputStream * out) {
+ ServiceInfo * s = services;
+ write_stringz(out, "E");
+ write_stringz(out, LOCATOR);
+ write_stringz(out, "Hello");
+ out->write(out, '[');
+ while (s) {
+ if (s != services) out->write(out, ',');
+ json_write_string(out, s->name);
+ s = s->next;
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+static void command_sync(char * token, InputStream * inp, OutputStream * out) {
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, 0);
+ out->write(out, MARKER_EOM);
+}
+
+void ini_protocol(void) {
+ add_command_handler(LOCATOR, "sync", command_sync);
+}
+
+
diff --git a/protocol.h b/protocol.h
new file mode 100644
index 00000000..13a4ee44
--- /dev/null
+++ b/protocol.h
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF communication protocol.
+ * 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.
+ */
+
+#ifndef D_protocol
+#define D_protocol
+
+#include "streams.h"
+
+typedef void (*MessageHandler)(char * /* Token or NULL */, InputStream *, OutputStream *);
+
+/*
+ * Current communication channels congestion level.
+ * Can be used by message handlers to manage congestion.
+ */
+extern int congestion_level;
+
+/*
+ * Read and dispatch one protocol message.
+ * This function is by channel manager when a message is available for handling.
+ */
+extern void handle_protocol_message(InputStream * inp, OutputStream * out);
+
+/*
+ * Send "Hello" message to host.
+ * This function is called by channel manager when new channel is opened.
+ */
+extern void send_hello_message(OutputStream * out);
+
+/*
+ * Register command message handler.
+ * The handler will be called for each incoming command message,
+ * that belongs to 'service' and has name 'name'.
+ */
+extern void add_command_handler(const char * service, const char * name, MessageHandler handler);
+
+/*
+ * Register event message handler.
+ * The handler will be called for each incoming event message,
+ * that belongs to 'service' and has name 'name'.
+ */
+extern void add_event_handler(const char * service, const char * name, MessageHandler handler);
+
+extern void ini_protocol(void);
+
+#endif
diff --git a/proxy.c b/proxy.c
new file mode 100644
index 00000000..ad244778
--- /dev/null
+++ b/proxy.c
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements 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.
+ */
+
+// TODO: Proxy service is not implemented yet
+
+#include "proxy.h"
+
+void ini_proxy_service(void) {
+}
diff --git a/proxy.h b/proxy.h
new file mode 100644
index 00000000..208b5fde
--- /dev/null
+++ b/proxy.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * This module implements 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.
+ */
+
+#ifndef D_proxy
+#define D_proxy
+
+extern void ini_proxy_service(void);
+
+#endif
+
diff --git a/registers.c b/registers.c
new file mode 100644
index 00000000..c524de44
--- /dev/null
+++ b/registers.c
@@ -0,0 +1,376 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF Registers - CPU registers access service.
+ */
+
+#include "config.h"
+#if SERVICE_Registers
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "mdep.h"
+#include "protocol.h"
+#include "context.h"
+#include "json.h"
+#include "exceptions.h"
+#include "registers.h"
+
+static const char * REGISTERS = "Registers";
+
+#if defined(_WRS_KERNEL)
+
+# define regs_index taskRegName
+# if defined(_WRS_REG_INDEX_REGWIDTH) || (CPU_FAMILY==COLDFIRE)
+# define REG_WIDTH(x) (x).regWidth
+# else
+# define REG_WIDTH(x) 4
+# endif
+
+#else
+
+typedef struct {
+ char *regName; /* pointer to register name */
+ int regOff; /* offset to entry in REG_SET */
+ int regWidth; /* register width in bytes */
+} REG_INDEX;
+
+#define REG_WIDTH(x) (x).regWidth
+
+#define REG_OFFSET(name) offsetof(REG_SET, name)
+
+#if defined(__i386__)
+static REG_INDEX regs_index[] = {
+ { "edi", REG_OFFSET(edi), 4},
+ { "esi", REG_OFFSET(esi), 4},
+ { "ebp", REG_OFFSET(ebp), 4},
+ { "esp", REG_OFFSET(esp), 4},
+ { "ebx", REG_OFFSET(ebx), 4},
+ { "edx", REG_OFFSET(edx), 4},
+ { "ecx", REG_OFFSET(ecx), 4},
+ { "eax", REG_OFFSET(eax), 4},
+ { "eflags", REG_OFFSET(eflags), 4},
+ { "eip", REG_OFFSET(eip), 4},
+ { NULL, 0, 0},
+};
+
+#else
+
+/* TODO: Linux: support for CPU types other then I86 */
+#error "Unknown CPU"
+
+#endif
+
+#endif /* _WRS_KERNEL */
+
+static short endianess_test = 0x0201;
+#define BIG_ENDIAN_DATA (*(char *)&endianess_test == 0x02)
+
+static void write_context(OutputStream * out, char * id, Context * ctx, REG_INDEX * idx) {
+ assert(!ctx->exited);
+
+ out->write(out, '{');
+
+ json_write_string(out, "ID");
+ out->write(out, ':');
+ json_write_string(out, id);
+
+ out->write(out, ',');
+ json_write_string(out, "ParentID");
+ out->write(out, ':');
+ json_write_string(out, thread_id(ctx));
+
+ out->write(out, ',');
+ json_write_string(out, "Name");
+ out->write(out, ':');
+ json_write_string(out, idx->regName);
+
+ out->write(out, ',');
+ json_write_string(out, "Readable");
+ out->write(out, ':');
+ json_write_boolean(out, 1);
+
+ out->write(out, ',');
+ json_write_string(out, "Writeable");
+ out->write(out, ':');
+ json_write_boolean(out, 1);
+
+ out->write(out, ',');
+ json_write_string(out, "Formats");
+ out->write(out, ':');
+ out->write(out, '[');
+ json_write_string(out, "Hex");
+ out->write(out, ',');
+ json_write_string(out, "Decimal");
+ out->write(out, ']');
+
+#if !defined(_WRS_KERNEL)
+ out->write(out, ',');
+ json_write_string(out, "ProcessID");
+ out->write(out, ':');
+ json_write_string(out, pid2id(ctx->mem, 0));
+#endif
+
+ out->write(out, ',');
+ json_write_string(out, "BigEndian");
+ out->write(out, ':');
+ json_write_boolean(out, BIG_ENDIAN_DATA);
+
+ out->write(out, '}');
+ out->write(out, 0);
+}
+
+static int id2register(char * id, Context ** ctx, REG_INDEX ** idx) {
+ int i;
+ char name[64];
+ *ctx = NULL;
+ *idx = NULL;
+ if (*id++ != 'R') {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ i = 0;
+ while (*id != '.') {
+ if (*id == 0) {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ name[i++] = *id++;
+ }
+ name[i++] = 0;
+ id++;
+ for (i = 0; regs_index[i].regName != NULL; i++) {
+ if (strcmp(regs_index[i].regName, name) == 0) break;
+ }
+ if (regs_index[i].regName == NULL) {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ *ctx = id2ctx(id);
+ *idx = regs_index + i;
+ return 0;
+}
+
+static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ Context * ctx = NULL;
+ REG_INDEX * idx = NULL;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ id2register(id, &ctx, &idx);
+
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ if (err == 0) {
+ write_context(out, id, ctx, idx);
+ }
+ else {
+ write_stringz(out, "null");
+ }
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ pid_t pid, parent;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ pid = id2pid(id, &parent);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ write_errno(out, 0);
+
+ out->write(out, '[');
+ if (pid != 0 && parent != 0) {
+ Context * ctx = context_find_from_pid(pid);
+ if (ctx != NULL) {
+ char t_id[128];
+ REG_INDEX * idx = regs_index;
+ strcpy(t_id, thread_id(ctx));
+ while (idx->regName != NULL) {
+ char r_id[128];
+ if (idx != regs_index) out->write(out, ',');
+ snprintf(r_id, sizeof(r_id), "R%s.%s", idx->regName, t_id);
+ json_write_string(out, r_id);
+ idx ++;
+ }
+ }
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ char fmt[256];
+ int hex = 0;
+ Context * ctx = NULL;
+ REG_INDEX * idx = NULL;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ json_read_string(inp, fmt, sizeof(fmt));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ id2register(id, &ctx, &idx);
+
+ if (ctx == NULL || idx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+ else if (!ctx->intercepted) err = ERR_IS_RUNNING;
+ else if (strcmp(fmt, "Hex") == 0) hex = 1;
+ else if (strcmp(fmt, "Decimal") != 0) err = ERR_INV_FORMAT;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ if (err == 0) {
+ int64 n = 0;
+ char val[64];
+ int val_len = 0;
+ assert(REG_WIDTH(*idx) <= sizeof(n));
+ memcpy( (char *)&n + (BIG_ENDIAN_DATA ? sizeof(n) - REG_WIDTH(*idx) : 0),
+ (char *)&ctx->regs + idx->regOff,
+ REG_WIDTH(*idx));
+ if (hex) {
+ while (val_len < REG_WIDTH(*idx) * 2) {
+ int i = (int)(n & 0xf);
+ val[val_len++] = i < 10 ? '0' + i : 'A' + i - 10;
+ n = n >> 4;
+ }
+ }
+ else {
+ int neg = n < 0;
+ uns64 m = neg ? -n : n;
+ do {
+ int i = (int)(m % 10);
+ val[val_len++] = '0' + i;
+ m = m / 10;
+ }
+ while (m != 0);
+ if (neg) val[val_len++] = '-';
+ }
+ out->write(out, '"');
+ while (val_len > 0) out->write(out, val[--val_len]);
+ out->write(out, '"');
+ out->write(out, 0);
+ }
+ else {
+ write_stringz(out, "null");
+ }
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_register_changed(OutputStream * out, char * id) {
+ write_stringz(out, "E");
+ write_stringz(out, REGISTERS);
+ write_stringz(out, "registerChanged");
+
+ json_write_string(out, id);
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_set(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ char fmt[256];
+ char val[256];
+ int hex = 0;
+ char * ptr = NULL;
+ Context * ctx = NULL;
+ REG_INDEX * idx = NULL;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ json_read_string(inp, fmt, sizeof(fmt));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ json_read_string(inp, val, sizeof(val));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ id2register(id, &ctx, &idx);
+
+ if (ctx == NULL || idx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+ else if (!ctx->intercepted) err = ERR_IS_RUNNING;
+ else if (strcmp(fmt, "Hex") == 0) hex = 1;
+ else if (strcmp(fmt, "Decimal") != 0) err = ERR_INV_FORMAT;
+
+ if (err == 0) {
+ int64 n = 0;
+ ptr = val;
+ if (hex) {
+ while (1) {
+ if (*ptr >= '0' && *ptr <= '9') n = (n << 4) | (int64)(*ptr++ - '0');
+ else if (*ptr >= 'A' && *ptr <= 'F') n = (n << 4) | (int64)(*ptr++ - 'A' + 10);
+ else if (*ptr >= 'a' && *ptr <= 'f') n = (n << 4) | (int64)(*ptr++ - 'a' + 10);
+ else break;
+ }
+ }
+ else {
+ uns64 m = 0;
+ int neg = *ptr == '-';
+ if (neg) ptr++;
+ while (1) {
+ if (*ptr >= '0' && *ptr <= '9') m = (m * 10) + (uns64)(*ptr++ - '0');
+ else break;
+ }
+ n = neg ? (~m + 1) : m;
+ }
+ if (*ptr != 0) {
+ err = ERR_INV_NUMBER;
+ }
+ else {
+ assert(REG_WIDTH(*idx) <= sizeof(n));
+ memcpy( (char *)&ctx->regs + idx->regOff,
+ (char *)&n + (BIG_ENDIAN_DATA ? sizeof(n) - REG_WIDTH(*idx) : 0),
+ REG_WIDTH(*idx));
+ ctx->regs_dirty = 1;
+ send_event_register_changed(out, id);
+ }
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+void ini_registers_service(void) {
+ add_command_handler(REGISTERS, "getContext", command_get_context);
+ add_command_handler(REGISTERS, "getChildren", command_get_children);
+ add_command_handler(REGISTERS, "get", command_get);
+ add_command_handler(REGISTERS, "set", command_set);
+}
+
+#endif
+
diff --git a/registers.h b/registers.h
new file mode 100644
index 00000000..3c7570a4
--- /dev/null
+++ b/registers.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * TCF Registers - CPU registers access service.
+ */
+
+#ifndef D_registers
+#define D_registers
+
+/*
+ * Initialize registers service.
+ */
+extern void ini_registers_service(void);
+
+
+#endif
diff --git a/runctrl.c b/runctrl.c
new file mode 100644
index 00000000..e88fce99
--- /dev/null
+++ b/runctrl.c
@@ -0,0 +1,773 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Target service implementation: run control (TCF name RunControl)
+ */
+
+#include "config.h"
+#if SERVICE_RunControl
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include "runctrl.h"
+#include "protocol.h"
+#include "channel.h"
+#include "json.h"
+#include "context.h"
+#include "myalloc.h"
+#include "trace.h"
+#include "events.h"
+#include "exceptions.h"
+#include "breakpoints.h"
+
+#define RM_RESUME 0
+#define RM_STEP_OVER 1
+#define RM_STEP_INTO 2
+#define RM_STEP_OVER_LINE 3
+#define RM_STEP_INTO_LINE 4
+#define RM_STEP_OUT 5
+
+#define STOP_ALL_TIMEOUT 1000000
+#define STOP_ALL_MAX_CNT 20
+
+static const char RUN_CONTROL[] = "RunControl";
+
+typedef struct SafeEvent SafeEvent;
+
+struct SafeEvent {
+ void (*done)(void *);
+ void * arg;
+ SafeEvent * next;
+};
+
+typedef struct GetContextArgs GetContextArgs;
+
+struct GetContextArgs {
+ OutputStream * out;
+ char token[256];
+ Context * ctx;
+ pid_t parent;
+};
+
+static SafeEvent * safe_event_list = NULL;
+static int safe_event_pid_count = 0;
+static int safe_event_generation = 0;
+
+#if !defined(WIN32) && !defined(_WRS_KERNEL)
+static char *get_executable(pid_t pid) {
+ static char s[FILE_PATH_SIZE + 1];
+ char tmpbuf[100];
+ int sz;
+
+ snprintf(tmpbuf, sizeof(tmpbuf), "/proc/%d/exe", pid);
+ if ((sz = readlink(tmpbuf, s, FILE_PATH_SIZE)) < 0) {
+ trace(LOG_ALWAYS, "error: readlink() failed; pid %d, error %d %s",
+ pid, errno, errno_to_str(errno));
+ return NULL;
+ }
+ s[sz] = 0;
+ return s;
+}
+#endif
+
+static void write_context(OutputStream * out, Context * ctx, int is_thread) {
+ assert(!ctx->exited);
+
+ out->write(out, '{');
+
+ json_write_string(out, "ID");
+ out->write(out, ':');
+ json_write_string(out, is_thread ? thread_id(ctx) : container_id(ctx));
+
+ if (is_thread) {
+ out->write(out, ',');
+ json_write_string(out, "ParentID");
+ out->write(out, ':');
+ json_write_string(out, container_id(ctx));
+ }
+
+#if !defined(_WRS_KERNEL)
+ out->write(out, ',');
+ json_write_string(out, "ProcessID");
+ out->write(out, ':');
+ json_write_string(out, pid2id(ctx->mem, 0));
+#endif
+
+#if !defined(WIN32) && !defined(_WRS_KERNEL)
+ if (!ctx->exiting && !is_thread) {
+ out->write(out, ',');
+ json_write_string(out, "File");
+ out->write(out, ':');
+ json_write_string(out, get_executable(ctx->pid));
+ }
+#endif
+
+ if (is_thread) {
+ out->write(out, ',');
+ json_write_string(out, "CanSuspend");
+ out->write(out, ':');
+ json_write_boolean(out, 1);
+
+ out->write(out, ',');
+ json_write_string(out, "CanResume");
+ out->write(out, ':');
+ json_write_long(out, (1 << RM_RESUME) | (1 << RM_STEP_INTO));
+
+ out->write(out, ',');
+ json_write_string(out, "HasState");
+ out->write(out, ':');
+ json_write_boolean(out, 1);
+ }
+
+ out->write(out, '}');
+}
+
+static void write_context_state(OutputStream * out, Context * ctx) {
+ char reason[128];
+
+ assert(!ctx->exited);
+
+ if (!ctx->intercepted) {
+ write_stringz(out, "0");
+ write_stringz(out, "null");
+ write_stringz(out, "null");
+ return;
+ }
+
+ /* Number: PC */
+ json_write_ulong(out, get_regs_PC(ctx->regs));
+ out->write(out, 0);
+
+ /* String: Reason */
+ if (ctx->event != 0) {
+ assert(ctx->signal == SIGTRAP);
+ snprintf(reason, sizeof(reason), "Event: %s", event_name(ctx->event));
+ }
+ else if (is_stopped_by_breakpoint(ctx)) {
+ strcpy(reason, "Breakpoint");
+ }
+ else if (ctx->signal == SIGSTOP || ctx->signal == SIGTRAP) {
+ strcpy(reason, "Suspended");
+ }
+ else if (signal_name(ctx->signal)) {
+ snprintf(reason, sizeof(reason), "Signal %d %s", ctx->signal, signal_name(ctx->signal));
+ }
+ else {
+ snprintf(reason, sizeof(reason), "Signal %d", ctx->signal);
+ }
+ json_write_string(out, reason);
+ out->write(out, 0);
+
+ /* Object: Aditional info */
+ out->write(out, '{');
+ json_write_string(out, "Event");
+ out->write(out, ':');
+ json_write_long(out, ctx->event);
+ out->write(out, ',');
+ json_write_string(out, "Signal");
+ out->write(out, ':');
+ json_write_long(out, ctx->signal);
+ if (signal_name(ctx->signal)) {
+ out->write(out, ',');
+ json_write_string(out, "SignalName");
+ out->write(out, ':');
+ json_write_string(out, signal_name(ctx->signal));
+ }
+ out->write(out, '}');
+ out->write(out, 0);
+}
+
+static void event_get_context(void * arg) {
+ GetContextArgs * s = (GetContextArgs *)arg;
+ OutputStream * out = s->out;
+ Context * ctx = s->ctx;
+
+ if (!is_stream_closed(out)) {
+ int err = 0;
+
+ write_stringz(out, "R");
+ write_stringz(out, s->token);
+
+ if (ctx->exited) err = ERR_ALREADY_EXITED;
+ write_errno(out, err);
+
+ if (err == 0) {
+ write_context(out, ctx, s->parent != 0);
+ out->write(out, 0);
+ }
+ else {
+ write_stringz(out, "null");
+ }
+
+ out->write(out, MARKER_EOM);
+ out->flush(out);
+ }
+ stream_unlock(out);
+ context_unlock(ctx);
+ loc_free(s);
+}
+
+static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char id[256];
+ Context * ctx = NULL;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ ctx = id2ctx(id);
+
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (err) {
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ write_stringz(out, "null");
+ out->write(out, MARKER_EOM);
+ }
+ else {
+ /* Need to stop everything to access context properties.
+ * In particular, proc FS access can fail when process is running.
+ */
+ GetContextArgs * s = loc_alloc_zero(sizeof(GetContextArgs));
+ s->out = out;
+ stream_lock(out);
+ strcpy(s->token, token);
+ s->ctx = ctx;
+ context_lock(ctx);
+ id2pid(id, &s->parent);
+ post_safe_event(event_get_context, s);
+ }
+}
+
+static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ write_errno(out, 0);
+
+ out->write(out, '[');
+ if (id[0] == 0) {
+ LINK * qp;
+ int cnt = 0;
+ for (qp = context_root.next; qp != &context_root; qp = qp->next) {
+ Context * ctx = ctxl2ctxp(qp);
+ if (ctx->exited) continue;
+ if (ctx->parent != NULL) continue;
+ if (cnt > 0) out->write(out, ',');
+ json_write_string(out, container_id(ctx));
+ cnt++;
+ }
+ }
+ else if (id[0] == 'P') {
+ LINK * qp;
+ int cnt = 0;
+ pid_t ppd = 0;
+ pid_t pid = id2pid(id, &ppd);
+ Context * parent = id2ctx(id);
+ if (parent != NULL && parent->parent == NULL && ppd == 0) {
+ if (!parent->exited) {
+ if (cnt > 0) out->write(out, ',');
+ json_write_string(out, thread_id(parent));
+ cnt++;
+ }
+ for (qp = parent->children.next; qp != &parent->children; qp = qp->next) {
+ Context * ctx = cldl2ctxp(qp);
+ if (ctx->exited) continue;
+ assert(ctx->parent == parent);
+ if (cnt > 0) out->write(out, ',');
+ json_write_string(out,thread_id(ctx));
+ cnt++;
+ }
+ }
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_state(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ Context * ctx;
+ int err = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ ctx = id2ctx(id);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+ write_errno(out, err);
+
+ json_write_boolean(out, ctx != NULL && ctx->intercepted);
+ out->write(out, 0);
+
+ if (err) {
+ write_stringz(out, "0");
+ write_stringz(out, "null");
+ write_stringz(out, "null");
+ }
+ else {
+ write_context_state(out, ctx);
+ }
+
+ out->write(out, MARKER_EOM);
+}
+
+static void send_simple_result(OutputStream * out, char * token, int err) {
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_context_resumed(OutputStream * out, Context * ctx);
+
+static void done_skip_breakpoint(SkipBreakpointInfo * s) {
+ OutputStream * out = s->out;
+ if (!is_stream_closed(out)) {
+ send_simple_result(out, s->token, s->error);
+ out->flush(out);
+ }
+}
+
+static void command_resume(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ long mode;
+ long count;
+ Context * ctx;
+ int err = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ mode = json_read_long(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ count = json_read_long(inp);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ ctx = id2ctx(id);
+ assert(safe_event_list == NULL);
+
+ if (ctx == NULL) {
+ err = ERR_INV_CONTEXT;
+ }
+ else if (ctx->exited) {
+ err = ERR_ALREADY_EXITED;
+ }
+ else if (!ctx->intercepted) {
+ err = ERR_ALREADY_RUNNING;
+ }
+ else if (ctx->regs_error) {
+ err = ctx->regs_error;
+ }
+ else if (count != 1) {
+ err = EINVAL;
+ }
+ else if (mode == RM_RESUME || mode == RM_STEP_INTO) {
+ SkipBreakpointInfo * sb = skip_breakpoint(ctx);
+ send_event_context_resumed(&broadcast_stream, ctx);
+ if (sb != NULL) {
+ if (mode == RM_STEP_INTO) sb->pending_intercept = 1;
+ sb->done = done_skip_breakpoint;
+ sb->out = out;
+ stream_lock(out);
+ strcpy(sb->token, token);
+ return;
+ }
+ if (mode == RM_STEP_INTO) {
+ if (context_single_step(ctx) < 0) {
+ err = errno;
+ }
+ else {
+ ctx->pending_intercept = 1;
+ }
+ }
+ else {
+ if (context_continue(ctx) < 0) err = errno;
+ }
+ }
+ else {
+ err = EINVAL;
+ }
+
+ send_simple_result(out, token, err);
+}
+
+static void send_event_context_suspended(OutputStream * out, Context * ctx);
+
+static void command_suspend(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ Context * ctx;
+ int err = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ ctx = id2ctx(id);
+
+ if (ctx == NULL) {
+ err = ERR_INV_CONTEXT;
+ }
+ else if (ctx->exited) {
+ err = ERR_ALREADY_EXITED;
+ }
+ else if (ctx->intercepted) {
+ err = ERR_ALREADY_STOPPED;
+ }
+ else if (ctx->stopped) {
+ send_event_context_suspended(&broadcast_stream, ctx);
+ }
+ else {
+ ctx->pending_intercept = 1;
+ if (context_stop(ctx) < 0) err = errno;
+ }
+
+ send_simple_result(out, token, err);
+}
+
+static void command_not_supported(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ send_simple_result(out, token, ENOSYS);
+}
+
+static void send_event_context_added(OutputStream * out, Context * ctx) {
+ write_stringz(out, "E");
+ write_stringz(out, RUN_CONTROL);
+ write_stringz(out, "contextAdded");
+
+ /* <array of context data> */
+ out->write(out, '[');
+ if (ctx->parent == NULL) {
+ write_context(out, ctx, 0);
+ out->write(out, ',');
+ }
+ write_context(out, ctx, 1);
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_context_changed(OutputStream * out, Context * ctx) {
+ write_stringz(out, "E");
+ write_stringz(out, RUN_CONTROL);
+ write_stringz(out, "contextChanged");
+
+ /* <array of context data> */
+ out->write(out, '[');
+ if (ctx->parent == NULL) {
+ write_context(out, ctx, 0);
+ out->write(out, ',');
+ }
+ write_context(out, ctx, 1);
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_context_removed(OutputStream * out, Context * ctx) {
+ write_stringz(out, "E");
+ write_stringz(out, RUN_CONTROL);
+ write_stringz(out, "contextRemoved");
+
+ /* <array of context IDs> */
+ out->write(out, '[');
+ json_write_string(out, thread_id(ctx));
+ if (ctx->parent == NULL && list_is_empty(&ctx->children)) {
+ out->write(out, ',');
+ json_write_string(out, container_id(ctx));
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_context_suspended(OutputStream * out, Context * ctx) {
+ assert(!ctx->exited);
+ assert(!ctx->intercepted);
+ ctx->intercepted = 1;
+ ctx->pending_intercept = 0;
+
+ write_stringz(out, "E");
+ write_stringz(out, RUN_CONTROL);
+ write_stringz(out, "contextSuspended");
+
+ /* String: Context ID */
+ json_write_string(out, thread_id(ctx));
+ out->write(out, 0);
+
+ write_context_state(out, ctx);
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_context_resumed(OutputStream * out, Context * ctx) {
+ assert(ctx->intercepted);
+ assert(!ctx->pending_intercept);
+ ctx->intercepted = 0;
+
+ write_stringz(out, "E");
+ write_stringz(out, RUN_CONTROL);
+ write_stringz(out, "contextResumed");
+
+ /* String: Context ID */
+ json_write_string(out, thread_id(ctx));
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+static void send_event_context_exception(OutputStream * out, Context * ctx) {
+ char buf[128];
+
+ write_stringz(out, "E");
+ write_stringz(out, RUN_CONTROL);
+ write_stringz(out, "contextException");
+
+ /* String: Context ID */
+ json_write_string(out, thread_id(ctx));
+ out->write(out, 0);
+
+ /* String: Human readable description of the exception */
+ snprintf(buf, sizeof(buf), "Signal %d", ctx->signal);
+ json_write_string(out, buf);
+ out->write(out, 0);
+
+ out->write(out, MARKER_EOM);
+}
+
+int is_all_stopped(void) {
+ LINK * qp;
+ for (qp = context_root.next; qp != &context_root; qp = qp->next) {
+ Context * ctx = ctxl2ctxp(qp);
+ if (ctx->exited || ctx->exiting) continue;
+ if (!ctx->stopped) return 0;
+ }
+ return are_channels_suspended();
+}
+
+static void continue_temporary_stopped(void * arg) {
+ LINK * qp;
+
+ if ((int)arg != safe_event_generation) return;
+ assert(safe_event_list == NULL);
+
+ if (channels_get_message_count() > 0) {
+ post_event(continue_temporary_stopped, (void *)safe_event_generation);
+ return;
+ }
+
+ for (qp = context_root.next; qp != &context_root; qp = qp->next) {
+ Context * ctx = ctxl2ctxp(qp);
+ if (ctx->exited) continue;
+ if (!ctx->stopped) continue;
+ if (ctx->intercepted) continue;
+ if (ctx->pending_step) continue;
+ context_continue(ctx);
+ }
+}
+
+static void run_safe_events(void * arg) {
+ LINK * qp;
+
+ if ((int)arg != safe_event_generation) return;
+ assert(safe_event_list != NULL);
+ assert(are_channels_suspended());
+
+ for (qp = context_root.next; qp != &context_root; qp = qp->next) {
+ Context * ctx = ctxl2ctxp(qp);
+ if (ctx->exited || ctx->exiting) continue;
+ if (!ctx->pending_step) {
+ int error = 0;
+ if (ctx->stopped) continue;
+ if (context_stop(ctx) < 0) {
+ error = errno;
+#ifdef _WRS_KERNEL
+ if (error == S_vxdbgLib_INVALID_CTX) {
+ /* Most often this means that context has exited,
+ * but exit event is not delivered yet.
+ * Not an error. */
+ error = 0;
+ }
+#endif
+ }
+ if (error) {
+ trace(LOG_ALWAYS, "error: can't temporary stop pid %d; error %d: %s",
+ ctx->pid, error, errno_to_str(error));
+ }
+ }
+ if (!ctx->pending_safe_event) {
+ ctx->pending_safe_event = 1;
+ safe_event_pid_count++;
+ }
+ else if (ctx->pending_safe_event == STOP_ALL_MAX_CNT) {
+ trace(LOG_ALWAYS, "error: can't temporary stop pid %d; error: timeout", ctx->pid);
+ ctx->exiting = 1;
+ ctx->pending_safe_event = 0;
+ safe_event_pid_count--;
+ }
+ else {
+ ctx->pending_safe_event++;
+ }
+ }
+
+ if ((int)arg != safe_event_generation) return;
+
+ while (safe_event_list) {
+ SafeEvent * i = safe_event_list;
+ if (safe_event_pid_count > 0) {
+ post_event_with_delay(run_safe_events, (void *)++safe_event_generation, STOP_ALL_TIMEOUT);
+ return;
+ }
+ assert(is_all_stopped());
+ safe_event_list = i->next;
+ i->done(i->arg);
+ loc_free(i);
+ if ((int)arg != safe_event_generation) return;
+ }
+
+ channels_resume();
+ /* Lazily continue execution of temporary stopped contexts */
+ post_event(continue_temporary_stopped, (void *)safe_event_generation);
+}
+
+static void check_safe_events(Context * ctx) {
+ assert(ctx->stopped || ctx->exited);
+ assert(ctx->pending_safe_event);
+ assert(safe_event_list != NULL);
+ assert(safe_event_pid_count > 0);
+ ctx->pending_safe_event = 0;
+ safe_event_pid_count--;
+ if (safe_event_pid_count == 0) {
+ post_event(run_safe_events, (void *)++safe_event_generation);
+ }
+}
+
+void post_safe_event(void (*done)(void *), void * arg) {
+ SafeEvent * i = (SafeEvent *)loc_alloc(sizeof(SafeEvent));
+ i->done = done;
+ i->arg = arg;
+ if (safe_event_list == NULL) {
+ assert(safe_event_pid_count == 0);
+ channels_suspend();
+ post_event(run_safe_events, (void *)++safe_event_generation);
+ }
+ assert(are_channels_suspended());
+ i->next = safe_event_list;
+ safe_event_list = i;
+}
+
+static void event_context_created(Context * ctx) {
+ assert(!ctx->exited);
+ assert(!ctx->intercepted);
+ assert(!ctx->stopped);
+ send_event_context_added(&broadcast_stream, ctx);
+ broadcast_stream.flush(&broadcast_stream);
+}
+
+static void event_context_changed(Context * ctx) {
+ send_event_context_changed(&broadcast_stream, ctx);
+ broadcast_stream.flush(&broadcast_stream);
+}
+
+static void event_context_stopped(Context * ctx) {
+ assert(ctx->stopped);
+ assert(!ctx->intercepted);
+ assert(!ctx->exited);
+ if (ctx->pending_safe_event) check_safe_events(ctx);
+ if (is_stopped_by_breakpoint(ctx)) {
+ if (evaluate_breakpoint_condition(ctx)) {
+ ctx->pending_intercept = 1;
+ }
+ else {
+ skip_breakpoint(ctx);
+ }
+ }
+ else if (ctx->signal != SIGSTOP && ctx->signal != SIGTRAP) {
+ send_event_context_exception(&broadcast_stream, ctx);
+ ctx->pending_intercept = 1;
+ }
+ if (ctx->pending_intercept) {
+ send_event_context_suspended(&broadcast_stream, ctx);
+ broadcast_stream.flush(&broadcast_stream);
+ }
+ if (!ctx->intercepted && safe_event_list == NULL) {
+ context_continue(ctx);
+ }
+}
+
+static void event_context_started(Context * ctx) {
+ assert(!ctx->stopped);
+ assert(!ctx->intercepted);
+ ctx->stopped_by_bp = 0;
+ if (safe_event_list) {
+ if (!ctx->pending_step) {
+ context_stop(ctx);
+ }
+ if (!ctx->pending_safe_event) {
+ ctx->pending_safe_event = 1;
+ safe_event_pid_count++;
+ }
+ }
+}
+
+static void event_context_exited(Context * ctx) {
+ assert(!ctx->stopped);
+ assert(!ctx->intercepted);
+ if (ctx->pending_safe_event) check_safe_events(ctx);
+ send_event_context_removed(&broadcast_stream, ctx);
+ broadcast_stream.flush(&broadcast_stream);
+}
+
+void ini_run_ctrl_service(void) {
+ static ContextEventListener listener = {
+ event_context_created,
+ event_context_exited,
+ event_context_stopped,
+ event_context_started,
+ event_context_changed,
+ NULL
+ };
+ add_context_event_listener(&listener);
+ add_command_handler(RUN_CONTROL, "getContext", command_get_context);
+ add_command_handler(RUN_CONTROL, "getChildren", command_get_children);
+ add_command_handler(RUN_CONTROL, "getState", command_get_state);
+ add_command_handler(RUN_CONTROL, "resume", command_resume);
+ add_command_handler(RUN_CONTROL, "suspend", command_suspend);
+ add_command_handler(RUN_CONTROL, "terminate", command_not_supported);
+}
+
+#endif
diff --git a/runctrl.h b/runctrl.h
new file mode 100644
index 00000000..46d0d470
--- /dev/null
+++ b/runctrl.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Target service implementation: run control (TCF name RunControl)
+ */
+
+#ifndef D_runctrl
+#define D_runctrl
+
+#include "mdep.h"
+#include "context.h"
+
+/*
+ * Add "safe" event.
+ * Temporary suspends handling of incoming messages and stops all debuggee threads.
+ * Callback function 'done' will be called when everything is stopped and
+ * it is safe to access debuggee memory, plant breakpoints, etc.
+ */
+extern void post_safe_event(void (*done)(void *), void * arg);
+
+/*
+ * Return 1 if all threads in debuggee are stopped and handling of incoming messages
+ * is suspended and it is safe to access debuggee memory, plant breakpoints, etc.
+ */
+extern int is_all_stopped(void);
+
+/*
+ * Initialize run control service.
+ */
+extern void ini_run_ctrl_service(void);
+
+#endif
diff --git a/stacktrace.c b/stacktrace.c
new file mode 100644
index 00000000..d0c64834
--- /dev/null
+++ b/stacktrace.c
@@ -0,0 +1,425 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Target service implementation: stack trace (TCF name StackTrace)
+ */
+
+#include "config.h"
+#if SERVICE_StackTrace
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "mdep.h"
+#include "myalloc.h"
+#include "protocol.h"
+#include "context.h"
+#include "json.h"
+#include "exceptions.h"
+#include "stacktrace.h"
+
+static const char * STACKTRACE = "StackTrace";
+
+struct StackFrame {
+ unsigned long fp; /* frame address */
+ unsigned long pc; /* return address */
+ unsigned long fn; /* address of function */
+ int arg_cnt; /* number of function arguments */
+ unsigned long args; /* address of function arguments */
+};
+
+struct StackTrace {
+ int error;
+ int frame_cnt;
+ int top_first;
+ struct StackFrame frames[1];
+};
+
+typedef struct StackFrame StackFrame;
+typedef struct StackTrace StackTrace;
+
+typedef void (*STACK_TRACE_CALLBAK)(
+ void *, /* address from which function was called */
+ int , /* address of function called */
+ int , /* number of arguments in function call */
+ int * , /* pointer to function args */
+ int , /* thread ID */
+ int /* TRUE if Kernel addresses */
+);
+
+static int stack_trace_max = 0;
+static StackTrace * stack_trace = NULL;
+
+static void stack_trace_callback(
+ void * callAdrs, /* address from which function was called */
+ int funcAdrs, /* address of function called */
+ int nargs, /* number of arguments in function call */
+ int * args, /* pointer to function args */
+ int taskId, /* task's ID */
+ int isKernelAdrs /* TRUE if Kernel addresses */
+)
+{
+ StackFrame * f;
+ if (stack_trace == NULL) {
+ stack_trace_max = 64;
+ stack_trace = (StackTrace *)loc_alloc(sizeof(StackTrace) + (stack_trace_max - 1) * sizeof(StackFrame));
+ memset(stack_trace, 0, sizeof(StackTrace));
+ }
+ else if (stack_trace->frame_cnt >= stack_trace_max) {
+ stack_trace_max *= 2;
+ stack_trace = (StackTrace *)loc_realloc(stack_trace, sizeof(StackTrace) + (stack_trace_max - 1) * sizeof(StackFrame));
+ }
+ f = stack_trace->frames + stack_trace->frame_cnt++;
+ memset(f, 0, sizeof(StackFrame));
+ f->pc = (unsigned long)callAdrs;
+ f->fn = (unsigned long)funcAdrs;
+ f->arg_cnt = nargs;
+ f->args = (unsigned long)args;
+}
+
+#if defined(_WRS_KERNEL)
+
+#include <trcLib.h>
+
+static void trace_stack(Context * ctx, STACK_TRACE_CALLBAK callback) {
+ trcStack(&ctx->regs, (FUNCPTR)stack_trace_callback, ctx->pid);
+}
+
+#else
+
+#define MAX_FRAMES 1000
+
+#define JMPD08 0xeb
+#define JMPD32 0xe9
+#define PUSH_EBP 0x55
+#define MOV_ESP0 0x89
+#define MOV_ESP1 0xe5
+#define ENTER 0xc8
+#define RET 0xc3
+#define RETADD 0xc2
+
+/*
+ * trace_jump - resolve any JMP instructions to final destination
+ *
+ * This routine returns a pointer to the next non-JMP instruction to be
+ * executed if the pc were at the specified <adrs>. That is, if the instruction
+ * at <adrs> is not a JMP, then <adrs> is returned. Otherwise, if the
+ * instruction at <adrs> is a JMP, then the destination of the JMP is
+ * computed, which then becomes the new <adrs> which is tested as before.
+ * Thus we will eventually return the address of the first non-JMP instruction
+ * to be executed.
+ *
+ * The need for this arises because compilers may put JMPs to instructions
+ * that we are interested in, instead of the instruction itself. For example,
+ * optimizers may replace a stack pop with a JMP to a stack pop. Or in very
+ * UNoptimized code, the first instruction of a subroutine may be a JMP to
+ * a PUSH %EBP MOV %ESP %EBP, instead of a PUSH %EBP MOV %ESP %EBP (compiler
+ * may omit routine "post-amble" at end of parsing the routine!). We call
+ * this routine anytime we are looking for a specific kind of instruction,
+ * to help handle such cases.
+ *
+ * RETURNS: The address that a chain of branches points to.
+ */
+static unsigned long trace_jump(Context * ctx, unsigned long addr) {
+ int cnt = 0;
+ /* while instruction is a JMP, get destination adrs */
+ while (cnt < 100) {
+ unsigned char instr; /* instruction opcode at <addr> */
+ unsigned long dest; /* Jump destination address */
+ if (context_read_mem(ctx, addr, &instr, 1) < 0) return addr;
+
+ /* If instruction is a JMP, get destination adrs */
+ if (instr == JMPD08) {
+ signed char disp08;
+ if (context_read_mem(ctx, addr + 1, &disp08, 1) < 0) return addr;
+ dest = addr + 2 + disp08;
+ }
+ else if (instr == JMPD32) {
+ int disp32;
+ assert(sizeof(disp32) == 4);
+ if (context_read_mem(ctx, addr + 1, &disp32, 4) < 0) return addr;
+ dest = addr + 5 + disp32;
+ }
+ else {
+ break;
+ }
+ if (dest == addr) break;
+ addr = dest;
+ cnt++;
+ }
+ return addr;
+}
+
+static int trace_stack(Context * ctx, STACK_TRACE_CALLBAK callback) {
+ unsigned long pc = ctx->regs.eip;
+ unsigned long fp = ctx->regs.ebp;
+ unsigned long fp_prev = 0;
+
+ unsigned long addr = trace_jump(ctx, pc);
+ unsigned char code[4];
+ unsigned cnt = 0;
+
+ /*
+ * we don't have a stack frame in a few restricted but useful cases:
+ * 1) we are at a PUSH %EBP MOV %ESP %EBP or RET or ENTER instruction,
+ * 2) we are the first instruction of a subroutine (this may NOT be
+ * a PUSH %EBP MOV %ESP %EBP instruction with some compilers)
+ */
+ if (context_read_mem(ctx, addr - 1, code, sizeof(code)) < 0) return -1;
+
+ if (code[1] == PUSH_EBP && code[2] == MOV_ESP0 && code[3] == MOV_ESP1 ||
+ code[1] == ENTER || code[1] == RET || code[1] == RETADD) {
+ fp_prev = fp;
+ fp = ctx->regs.esp - 4;
+ }
+ else if (code[0] == PUSH_EBP && code[1] == MOV_ESP0 && code[2] == MOV_ESP1) {
+ fp_prev = fp;
+ fp = ctx->regs.esp;
+ }
+
+ assert(stack_trace == NULL || stack_trace->frame_cnt == 0);
+ while (fp != 0 && cnt < MAX_FRAMES) {
+ unsigned long frame[2];
+ unsigned long fp_next;
+ if (context_read_mem(ctx, fp, frame, sizeof(frame)) < 0) return -1;
+ callback((void *)frame[1], 0, 0, 0, ctx->pid, 0);
+ stack_trace->frames[stack_trace->frame_cnt - 1].fp = fp;
+ stack_trace->top_first = 1;
+ cnt++;
+ fp_next = fp_prev != 0 ? fp_prev : frame[0];
+ fp_prev = 0;
+ if (fp_next <= fp) break;
+ fp = fp_next;
+ }
+
+ return 0;
+}
+
+#endif
+
+static void create_stack_trace(Context * ctx) {
+ stack_trace = NULL;
+ stack_trace_max = 0;
+ if (ctx->regs_error != 0) {
+ stack_trace = (StackTrace *)loc_alloc_zero(sizeof(StackTrace));
+ stack_trace->error = ctx->regs_error;
+ }
+ else {
+ trace_stack(ctx, stack_trace_callback);
+ }
+ ctx->stack_trace = stack_trace;
+ stack_trace = NULL;
+}
+
+static int id2frame(char * id, Context ** ctx, int * idx) {
+ int i;
+ char pid[64];
+ int frame;
+ StackTrace * s = NULL;
+
+ *ctx = NULL;
+ *idx = 0;
+ if (*id++ != 'F') {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ if (*id++ != 'P') {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ i = 0;
+ while (*id != '.') {
+ if (*id == 0) {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ pid[i++] = *id++;
+ }
+ pid[i++] = 0;
+ id++;
+ *ctx = context_find_from_pid(strtol(pid, NULL, 10));
+ s = (*ctx)->stack_trace;
+ if (s == NULL) {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ frame = strtol(id, NULL, 10);
+ *idx = s->top_first ? s->frame_cnt - frame - 1 : frame;
+ return 0;
+}
+
+static void write_context(OutputStream * out, char * id, Context * ctx, int level, StackFrame * frame) {
+ out->write(out, '{');
+
+ json_write_string(out, "ID");
+ out->write(out, ':');
+ json_write_string(out, id);
+
+ out->write(out, ',');
+ json_write_string(out, "ParentID");
+ out->write(out, ':');
+ json_write_string(out, thread_id(ctx));
+
+#if !defined(_WRS_KERNEL)
+ out->write(out, ',');
+ json_write_string(out, "ProcessID");
+ out->write(out, ':');
+ json_write_string(out, pid2id(ctx->mem, 0));
+#endif
+
+ if (frame->fp) {
+ out->write(out, ',');
+ json_write_string(out, "FP");
+ out->write(out, ':');
+ json_write_ulong(out, frame->fp);
+ }
+
+ if (frame->pc) {
+ out->write(out, ',');
+ json_write_string(out, "RP");
+ out->write(out, ':');
+ json_write_ulong(out, frame->pc);
+ }
+
+ if (frame->arg_cnt) {
+ out->write(out, ',');
+ json_write_string(out, "ArgsCnt");
+ out->write(out, ':');
+ json_write_ulong(out, frame->arg_cnt);
+ }
+
+ if (frame->args) {
+ out->write(out, ',');
+ json_write_string(out, "ArgsAddr");
+ out->write(out, ':');
+ json_write_ulong(out, frame->args);
+ }
+
+ out->write(out, '}');
+}
+
+static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
+ int err = 0;
+ char ** ids;
+ int id_cnt = 0;
+ int i;
+
+ ids = json_read_alloc_string_array(inp, &id_cnt);
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ out->write(out, '[');
+ for (i = 0; i < id_cnt; i++) {
+ StackTrace * s = NULL;
+ Context * ctx = NULL;
+ int idx = 0;
+ if (i > 0) out->write(out, ',');
+ if (id2frame(ids[i], &ctx, &idx) < 0) {
+ err = errno;
+ }
+ else if (!ctx->intercepted) {
+ err = ERR_IS_RUNNING;
+ }
+ else {
+ if (ctx->stack_trace == NULL) create_stack_trace(ctx);
+ s = (StackTrace *)ctx->stack_trace;
+ }
+ if (s == NULL || idx < 0 || idx >= s->frame_cnt) {
+ write_string(out, "null");
+ }
+ else {
+ int level = s->top_first ? s->frame_cnt - idx - 1 : idx;
+ write_context(out, ids[i], ctx, level, s->frames + idx);
+ }
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+ write_errno(out, err);
+ out->write(out, MARKER_EOM);
+ loc_free(ids);
+}
+
+static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ int err = 0;
+ pid_t pid, parent;
+ Context * ctx = NULL;
+ StackTrace * s = NULL;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ pid = id2pid(id, &parent);
+ if (pid != 0 && parent != 0) {
+ ctx = context_find_from_pid(pid);
+ if (ctx != NULL) {
+ if (!ctx->intercepted) {
+ err = ERR_IS_RUNNING;
+ }
+ else {
+ if (ctx->stack_trace == NULL) create_stack_trace(ctx);
+ s = (StackTrace *)ctx->stack_trace;
+ }
+ }
+ }
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ write_errno(out, s != NULL ? s->error : err);
+
+ if (s == NULL) {
+ write_stringz(out, "null");
+ }
+ else {
+ int i;
+ char frame_id[64];
+ out->write(out, '[');
+ for (i = 0; i < s->frame_cnt; i++) {
+ if (i > 0) out->write(out, ',');
+ snprintf(frame_id, sizeof(frame_id), "FP%d.%d", ctx->pid, i);
+ json_write_string(out, frame_id);
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+ }
+
+ out->write(out, MARKER_EOM);
+}
+
+static void delete_stack_trace(Context * ctx) {
+ if (ctx->stack_trace != NULL) {
+ loc_free(ctx->stack_trace);
+ ctx->stack_trace = NULL;
+ }
+}
+
+void ini_stack_trace_service(void) {
+ static ContextEventListener listener = {
+ NULL,
+ delete_stack_trace,
+ delete_stack_trace,
+ delete_stack_trace,
+ delete_stack_trace,
+ NULL
+ };
+ add_context_event_listener(&listener);
+ add_command_handler(STACKTRACE, "getContext", command_get_context);
+ add_command_handler(STACKTRACE, "getChildren", command_get_children);
+}
+
+#endif
+
diff --git a/stacktrace.h b/stacktrace.h
new file mode 100644
index 00000000..a47329a2
--- /dev/null
+++ b/stacktrace.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Target service implementation: stack trace (TCF name StackTrace)
+ */
+
+#ifndef D_stacktrace
+#define D_stacktrace
+
+/*
+ * Initialize stack trace service.
+ */
+extern void ini_stack_trace_service(void);
+
+
+#endif
diff --git a/streams.c b/streams.c
new file mode 100644
index 00000000..86fe4070
--- /dev/null
+++ b/streams.c
@@ -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
+ *******************************************************************************/
+
+/*
+ * Abstract byte stream. Bytes in the stream can be divided into groups - messages.
+ */
+
+#include "streams.h"
+
+void write_string(OutputStream * out, const char * str) {
+ while (*str) out->write(out, (*str++) & 0xff);
+}
+
+void write_stringz(OutputStream * out, const char * str) {
+ while (*str) out->write(out, (*str++) & 0xff);
+ out->write(out, 0);
+}
diff --git a/streams.h b/streams.h
new file mode 100644
index 00000000..e15e62e7
--- /dev/null
+++ b/streams.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Abstract byte stream. Bytes in the stream can be divided into groups - messages.
+ */
+
+#ifndef D_streams
+#define D_streams
+
+/*
+ * MARKER_EOM - end of message
+ * MARKER_EOS - end of stream
+ */
+#define MARKER_EOM (-1)
+#define MARKER_EOS (-2)
+#define MARKER_NULL (-3)
+
+typedef struct OutputStream OutputStream;
+
+struct OutputStream {
+ void (*write)(OutputStream * stream, int byte);
+ void (*flush)(OutputStream * stream);
+};
+
+typedef struct InputStream InputStream;
+
+struct InputStream {
+ int (*read)(InputStream * stream);
+ int (*peek)(InputStream * stream);
+};
+
+extern void write_string(OutputStream * out, const char * str);
+extern void write_stringz(OutputStream * out, const char * str);
+
+#endif
diff --git a/symbols.c b/symbols.c
new file mode 100644
index 00000000..132892ae
--- /dev/null
+++ b/symbols.c
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Symbols service.
+ */
+#include "config.h"
+#if SERVICE_Symbols
+
+#if defined(_WRS_KERNEL)
+# include <vxWorks.h>
+# include <symLib.h>
+# include <sysSymTbl.h>
+#elif defined(WIN32)
+#else
+# include <elf.h>
+# include <libelf.h>
+# include <fcntl.h>
+#endif
+
+#include <errno.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "errors.h"
+#include "elf.h"
+#include "myalloc.h"
+#include "symbols.h"
+
+#define SYM_HASH_SIZE 1023
+
+typedef struct SymbolTable SymbolTable;
+
+struct SymbolTable {
+ SymbolTable * next;
+ char * str;
+ int str_size;
+ int sym_cnt;
+ void * syms; /* pointer to ELF section data: Elf32_Sym* or Elf64_Sym* */
+ int hash[SYM_HASH_SIZE];
+ int * hash_next;
+};
+
+static int calc_hash(char * s) {
+ unsigned h = 0;
+ while (*s) {
+ unsigned g;
+ h = (h << 4) + *s++;
+ if (g = h & 0xf0000000) h ^= g >> 24;
+ h &= ~g;
+ }
+ return h % SYM_HASH_SIZE;
+}
+
+static void free_sym_cache(ELF_File * file) {
+ SymbolTable * tables = (SymbolTable *)file->sym_cache;
+ while (tables != NULL) {
+ SymbolTable * tbl = tables;
+ tables = tbl->next;
+ loc_free(tbl->hash_next);
+ loc_free(tbl);
+ }
+}
+
+static int load_tables(ELF_File * file) {
+ int error = 0;
+
+#ifdef ELFMAG
+ unsigned idx;
+ for (idx = 0; idx < file->section_cnt; idx++) {
+ ELF_Section * sym_sec = file->sections[idx];
+ if (sym_sec == NULL) continue;
+ if (sym_sec->type == SHT_SYMTAB && sym_sec->size > 0) {
+ int i;
+ ELF_Section * str_sec;
+ U1_T * str_data = NULL;
+ U1_T * sym_data = NULL;
+ SymbolTable * tbl = (SymbolTable *)loc_alloc_zero(sizeof(SymbolTable));
+ tbl->next = (SymbolTable *)file->sym_cache;
+ file->sym_cache = tbl;
+ if (sym_sec->link >= file->section_cnt || (str_sec = file->sections[sym_sec->link]) == NULL) {
+ error = EINVAL;
+ break;
+ }
+ if (elf_load(sym_sec, &sym_data) < 0) {
+ error = errno;
+ assert(error != 0);
+ break;
+ }
+ if (elf_load(str_sec, &str_data) < 0) {
+ error = errno;
+ assert(error != 0);
+ break;
+ }
+ tbl->str = (char *)str_data;
+ tbl->str_size = str_sec->size;
+ tbl->syms = sym_data;
+ tbl->sym_cnt = sym_sec->size / sizeof(Elf32_Sym);
+ tbl->hash_next = (int *)loc_alloc(tbl->sym_cnt * sizeof(int));
+ for (i = 0; i < tbl->sym_cnt; i++) {
+ Elf32_Sym * s = (Elf32_Sym *)tbl->syms + i;
+ assert(s->st_name < tbl->str_size);
+ if (s->st_name == 0) {
+ tbl->hash_next[i] = 0;
+ }
+ else {
+ int h = calc_hash(tbl->str + s->st_name);
+ tbl->hash_next[i] = tbl->hash[h];
+ tbl->hash[h] = i;
+ }
+ }
+ }
+ }
+
+#else
+ error = EINVAL;
+#endif
+
+ if (error != 0) {
+ free_sym_cache(file);
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+int find_symbol(Context * ctx, char * name, Symbol * sym) {
+ int error = 0;
+
+#if defined(WIN32)
+
+ memset(sym, 0, sizeof(Symbol));
+ error = EINVAL;
+
+#elif defined(_WRS_KERNEL)
+
+ char * ptr;
+ SYM_TYPE type;
+
+ memset(sym, 0, sizeof(Symbol));
+ if (symFindByName(sysSymTbl, name, &ptr, &type) != OK) {
+ error = errno;
+ if (error == S_symLib_SYMBOL_NOT_FOUND) error = ERR_SYM_NOT_FOUND;
+ assert(error != 0);
+ }
+ else {
+ sym->abs = 1;
+ sym->value = (unsigned long)ptr;
+
+ if (SYM_IS_UNDF(type)) sym->storage = "UNDEF";
+ else if (SYM_IS_COMMON(type)) sym->storage = "COMMON";
+ else if (SYM_IS_GLOBAL(type)) sym->storage = "GLOBAL";
+ else if (SYM_IS_LOCAL(type)) sym->storage = "LOCAL";
+
+ if (SYM_IS_TEXT(type)) sym->section = ".text";
+ else if (SYM_IS_DATA(type)) sym->section = ".data";
+ else if (SYM_IS_BSS(type)) sym->section = ".bss";
+ assert(!SYM_IS_ABS(type) || sym->section == NULL);
+ }
+
+#else
+
+ char fnm[FILE_PATH_SIZE];
+ int found = 0;
+ ELF_File * file;
+
+ memset(sym, 0, sizeof(Symbol));
+ snprintf(fnm, sizeof(fnm), "/proc/%d/exe", ctx->mem);
+ file = elf_open(fnm);
+ if (file == NULL) error = errno;
+
+ if (error == 0 && file->sym_cache == NULL) {
+ if (load_tables(file) < 0) error = errno;
+ }
+
+ if (error == 0) {
+ int h = calc_hash(name);
+ SymbolTable * tbl = (SymbolTable *)file->sym_cache;
+ while (tbl != NULL && !found) {
+ int n = tbl->hash[h];
+ while (n && !found) {
+ Elf32_Sym * s = (Elf32_Sym *)tbl->syms + n;
+ if (strcmp(name, tbl->str + s->st_name) == 0) {
+ found = 1;
+ sym->abs = 1;
+ sym->value = s->st_value;
+ switch (ELF32_ST_BIND(s->st_info)) {
+ case STB_LOCAL: sym->storage = "LOCAL"; break;
+ case STB_GLOBAL: sym->storage = "GLOBAL"; break;
+ case STB_WEAK: sym->storage = "WEAK"; break;
+ }
+ if (s->st_shndx > 0 && s->st_shndx < file->section_cnt) {
+ static char sec_name[128];
+ ELF_Section * sec = file->sections[s->st_shndx];
+ if (sec != NULL && sec->name != NULL) {
+ sym->section = strncpy(sec_name, sec->name, sizeof(sec_name));
+ }
+ }
+ }
+ n = tbl->hash_next[n];
+ }
+ tbl = tbl->next;
+ }
+ }
+
+ if (error == 0 && !found) error = ERR_SYM_NOT_FOUND;
+
+#endif
+
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+void ini_symbols_service(void) {
+ elf_add_close_listener(free_sym_cache);
+}
+
+#endif
+
diff --git a/symbols.h b/symbols.h
new file mode 100644
index 00000000..e8d1bf93
--- /dev/null
+++ b/symbols.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Symbols service.
+ */
+
+#ifndef D_symbols
+#define D_symbols
+
+#include "context.h"
+
+typedef struct Symbol Symbol;
+
+struct Symbol {
+ unsigned long value;
+ char * section;
+ char * storage;
+ int abs;
+};
+
+extern int find_symbol(Context * ctx, char * name, Symbol * sym);
+
+extern void ini_symbols_service(void);
+
+#endif
diff --git a/sysmon.c b/sysmon.c
new file mode 100644
index 00000000..f9b9163e
--- /dev/null
+++ b/sysmon.c
@@ -0,0 +1,657 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+#include "config.h"
+#if SERVICE_SysMonitor
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include "mdep.h"
+#include "sysmon.h"
+#include "protocol.h"
+#include "json.h"
+#include "context.h"
+#include "errors.h"
+
+static const char SYS_MON[] = "SysMonitor";
+
+#if defined(WIN32)
+# error "SysMonitor service is not supported for Windows"
+#elif defined(_WRS_KERNEL)
+# error "SysMonitor service is not supported for VxWorks"
+#endif
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <grp.h>
+#include <linux/param.h>
+
+#define BUF_EOF (-1)
+
+static char buf[1024];
+static int buf_fd = -1;
+static int buf_pos = 0;
+static int buf_len = 0;
+static int buf_ch = 0;
+
+static void next_ch(void) {
+ while (buf_len >= 0 && buf_pos >= buf_len) {
+ buf_pos = 0;
+ buf_len = read(buf_fd, buf, sizeof(buf));
+ if (buf_len == 0) buf_len = -1;
+ }
+ if (buf_len < 0) {
+ buf_ch = BUF_EOF;
+ }
+ else {
+ buf_ch = buf[buf_pos++];
+ }
+}
+
+static void first_ch(int fd) {
+ buf_fd = fd;
+ buf_pos = 0;
+ buf_len = 0;
+ next_ch();
+}
+
+static void write_string_array(OutputStream * out, int f) {
+ int cnt = 0;
+ first_ch(f);
+ out->write(out, '[');
+ while (buf_ch != BUF_EOF && buf_ch != 0) {
+ if (cnt > 0) out->write(out, ',');
+ out->write(out, '"');
+ do {
+ json_write_char(out, buf_ch);
+ next_ch();
+ }
+ while (buf_ch != BUF_EOF && buf_ch != 0);
+ next_ch();
+ out->write(out, '"');
+ cnt++;
+ }
+ out->write(out, ']');
+}
+
+static void write_context(OutputStream * out, char * id, char * parent_id, char * dir) {
+ char fnm[FILE_PATH_SIZE + 1];
+ int sz;
+ int f;
+
+ out->write(out, '{');
+
+ if (chdir(dir) >= 0) {
+ if ((sz = readlink("cwd", fnm, FILE_PATH_SIZE)) > 0) {
+ fnm[sz] = 0;
+ json_write_string(out, "CWD");
+ out->write(out, ':');
+ json_write_string(out, fnm);
+ out->write(out, ',');
+ }
+
+ if ((sz = readlink("root", fnm, FILE_PATH_SIZE)) > 0) {
+ fnm[sz] = 0;
+ json_write_string(out, "Root");
+ out->write(out, ':');
+ json_write_string(out, fnm);
+ out->write(out, ',');
+ }
+
+ f = open("stat", O_RDONLY);
+ if (f >= 0) {
+ struct_stat st;
+ if (fstat(f, &st) == 0) {
+ struct passwd * pwd;
+ struct group * grp;
+
+ json_write_string(out, "UID");
+ out->write(out, ':');
+ json_write_long(out, st.st_uid);
+ out->write(out, ',');
+
+ json_write_string(out, "UGID");
+ out->write(out, ':');
+ json_write_long(out, st.st_gid);
+ out->write(out, ',');
+
+ pwd = getpwuid(st.st_uid);
+ if (pwd != NULL) {
+ json_write_string(out, "UserName");
+ out->write(out, ':');
+ json_write_string(out, pwd->pw_name);
+ out->write(out, ',');
+ }
+
+ grp = getgrgid(st.st_gid);
+ if (grp != NULL) {
+ json_write_string(out, "GroupName");
+ out->write(out, ':');
+ json_write_string(out, grp->gr_name);
+ out->write(out, ',');
+ }
+ }
+
+ memset(buf, 0, sizeof(buf));
+ if ((sz = read(f, buf, sizeof(buf))) > 0) {
+ char * str = buf;
+ int pid = 0; // The process ID.
+ char * comm = fnm; // The filename of the executable, in parentheses. This is visible
+ // whether or not the executable is swapped out.
+ char state = 0; // 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.
+ int ppid = 0; // The PID of the parent.
+ int pgrp = 0; // The process group ID of the process.
+ int session = 0; // The session ID of the process.
+ int tty_nr = 0; // The tty the process uses.
+ int tpgid = 0; // The process group ID of the process which currently owns the tty that
+ // the process is connected to.
+ unsigned long flags = 0; // The kernel flags word of the process. For bit meanings, see the PF_*
+ // defines in <linux/sched.h>. Details depend on the kernel version.
+ unsigned long minflt = 0; // The number of minor faults the process has made which have not
+ // required loading a memory page from disk.
+ unsigned long cminflt = 0; // The number of minor faults that the process's waited-for children
+ // have made.
+ unsigned long majflt = 0; // The number of major faults the process has made which have required
+ // loading a memory page from disk.
+ unsigned long cmajflt = 0; // The number of major faults that the process's waited-for children
+ // have made.
+ unsigned long utime = 0; // The number of jiffies that this process has been scheduled in user
+ // mode.
+ unsigned long stime = 0; // The number of jiffies that this process has been scheduled in kernel
+ // mode.
+ long cutime = 0; // The number of jiffies that this process's waited-for children have
+ // been scheduled in user mode. (See also times(2).)
+ long cstime = 0; // The number of jiffies that this process's waited-for children have
+ // been scheduled in kernel mode.
+ long priority = 0; // The standard nice value, plus fifteen. The value is never negative
+ // in the kernel.
+ long nice = 0; // The nice value ranges from 19 (nicest) to -19 (not nice to others).
+ long dummy = 0; // This value is hard coded to 0 as a placeholder for a removed field.
+ long itrealvalue = 0; // The time in jiffies before the next SIGALRM is sent to the process
+ // due to an interval timer.
+ unsigned long starttime = 0;// The time in jiffies the process started after system boot.
+ unsigned long vsize = 0; // Virtual memory size in bytes.
+ long rss = 0; // Resident Set Size: number of pages the process has in real memory,
+ // minus 3 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.
+ unsigned long rlim = 0; // Current limit in bytes on the rss of the process (usually 4294967295
+ // on i386).
+ unsigned long startcode = 0;// The address above which program text can run.
+ unsigned long endcode = 0; // The address below which program text can run.
+ unsigned long startstack =0;// The address of the start of the stack.
+ unsigned long kstkesp = 0; // The current value of esp (stack pointer), as found in the kernel
+ // stack page for the process.
+ unsigned long kstkeip = 0; // The current EIP (instruction pointer).
+ unsigned long signal = 0; // The bitmap of pending signals.
+ unsigned long blocked = 0; // The bitmap of blocked signals.
+ unsigned long sigignore = 0;// The bitmap of ignored signals.
+ unsigned long sigcatch = 0; // The bitmap of caught signals.
+ unsigned long wchan = 0; // 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. (If you have an up-to-date /etc/psdatabase,
+ // then try ps -l to see the WCHAN field in action.)
+ unsigned long nswap = 0; // Number of pages swapped (not maintained).
+ unsigned long cnswap = 0; // Cumulative nswap for child processes (not maintained).
+ int exit_signal = 0; // Signal to be sent to parent when we die.
+ int processor = 0; // CPU number last executed on.
+ unsigned long rt_priority=0;// Real-time scheduling priority (see sched_setscheduler(2)).
+ unsigned long policy = 0; // Scheduling policy (see sched_setscheduler(2)).
+
+ assert(sz < sizeof(buf));
+ buf[sz] = 0;
+
+ pid = (int)strtol(str, &str, 10);
+ while (*str == ' ') str++;
+ if (*str == '(') str++;
+ sz = 0;
+ while (*str && *str != ')') comm[sz++] = *str++;
+ comm[sz] = 0;
+ if (*str == ')') str++;
+ while (*str == ' ') str++;
+
+ sscanf(str,
+ "%c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu",
+ &state, &ppid, &pgrp, &session, &tty_nr, &tpgid, &flags,
+ &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime, &cutime, &cstime,
+ &priority, &nice, &dummy, &itrealvalue, &starttime, &vsize, &rss, &rlim,
+ &startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore, &sigcatch,
+ &wchan, &nswap, &cnswap, &exit_signal, &processor, &rt_priority, &policy);
+
+ json_write_string(out, "PID");
+ out->write(out, ':');
+ json_write_long(out, pid);
+ out->write(out, ',');
+
+ json_write_string(out, "File");
+ out->write(out, ':');
+ json_write_string(out, comm);
+ out->write(out, ',');
+
+ json_write_string(out, "State");
+ out->write(out, ':');
+ out->write(out, '"');
+ json_write_char(out, state);
+ out->write(out, '"');
+ out->write(out, ',');
+
+ if (ppid > 0) {
+ json_write_string(out, "PPID");
+ out->write(out, ':');
+ json_write_long(out, ppid);
+ out->write(out, ',');
+ }
+
+ json_write_string(out, "PGRP");
+ out->write(out, ':');
+ json_write_long(out, pgrp);
+ out->write(out, ',');
+
+ json_write_string(out, "Session");
+ out->write(out, ':');
+ json_write_long(out, session);
+ out->write(out, ',');
+
+ if (tty_nr > 0) {
+ json_write_string(out, "TTY");
+ out->write(out, ':');
+ json_write_long(out, tty_nr);
+ out->write(out, ',');
+ }
+
+ if (tpgid > 0) {
+ json_write_string(out, "TGID");
+ out->write(out, ':');
+ json_write_long(out, tpgid);
+ out->write(out, ',');
+ }
+
+ json_write_string(out, "Flags");
+ out->write(out, ':');
+ json_write_ulong(out, flags);
+ out->write(out, ',');
+
+ json_write_string(out, "MinFlt");
+ out->write(out, ':');
+ json_write_ulong(out, minflt);
+ out->write(out, ',');
+
+ json_write_string(out, "CMinFlt");
+ out->write(out, ':');
+ json_write_ulong(out, cminflt);
+ out->write(out, ',');
+
+ json_write_string(out, "MajFlt");
+ out->write(out, ':');
+ json_write_ulong(out, majflt);
+ out->write(out, ',');
+
+ json_write_string(out, "CMajFlt");
+ out->write(out, ':');
+ json_write_ulong(out, cmajflt);
+ out->write(out, ',');
+
+ json_write_string(out, "UTime");
+ out->write(out, ':');
+ json_write_int64(out, (int64)utime * 1000 / HZ);
+ out->write(out, ',');
+
+ json_write_string(out, "STime");
+ out->write(out, ':');
+ json_write_int64(out, (int64)stime * 1000 / HZ);
+ out->write(out, ',');
+
+ json_write_string(out, "CUTime");
+ out->write(out, ':');
+ json_write_int64(out, (int64)cutime * 1000 / HZ);
+ out->write(out, ',');
+
+ json_write_string(out, "CSTime");
+ out->write(out, ':');
+ json_write_int64(out, (int64)cstime * 1000 / HZ);
+ out->write(out, ',');
+
+ json_write_string(out, "Priority");
+ out->write(out, ':');
+ json_write_long(out, (long)priority - 15);
+ out->write(out, ',');
+
+ if (nice != 0) {
+ json_write_string(out, "Nice");
+ out->write(out, ':');
+ json_write_long(out, nice);
+ out->write(out, ',');
+ }
+
+ if (itrealvalue != 0) {
+ json_write_string(out, "ITRealValue");
+ out->write(out, ':');
+ json_write_int64(out, (int64)itrealvalue * 1000 / HZ);
+ out->write(out, ',');
+ }
+
+ json_write_string(out, "StartTime");
+ out->write(out, ':');
+ json_write_int64(out, (int64)starttime * 1000 / HZ);
+ out->write(out, ',');
+
+ json_write_string(out, "VSize");
+ out->write(out, ':');
+ json_write_ulong(out, vsize);
+ out->write(out, ',');
+
+ json_write_string(out, "PSize");
+ out->write(out, ':');
+ json_write_ulong(out, getpagesize());
+ out->write(out, ',');
+
+ json_write_string(out, "RSS");
+ out->write(out, ':');
+ json_write_long(out, rss);
+ out->write(out, ',');
+
+ json_write_string(out, "RLimit");
+ out->write(out, ':');
+ json_write_ulong(out, rlim);
+ out->write(out, ',');
+
+ if (startcode != 0) {
+ json_write_string(out, "CodeStart");
+ out->write(out, ':');
+ json_write_ulong(out, startcode);
+ out->write(out, ',');
+ }
+
+ if (endcode != 0) {
+ json_write_string(out, "CodeEnd");
+ out->write(out, ':');
+ json_write_ulong(out, endcode);
+ out->write(out, ',');
+ }
+
+ if (startstack != 0) {
+ json_write_string(out, "StackStart");
+ out->write(out, ':');
+ json_write_ulong(out, startstack);
+ out->write(out, ',');
+ }
+
+ json_write_string(out, "Signals");
+ out->write(out, ':');
+ json_write_ulong(out, signal);
+ out->write(out, ',');
+
+ json_write_string(out, "SigBlock");
+ out->write(out, ':');
+ json_write_ulong(out, blocked);
+ out->write(out, ',');
+
+ json_write_string(out, "SigIgnore");
+ out->write(out, ':');
+ json_write_ulong(out, sigignore);
+ out->write(out, ',');
+
+ json_write_string(out, "SigCatch");
+ out->write(out, ':');
+ json_write_ulong(out, sigcatch);
+ out->write(out, ',');
+
+ if (wchan != 0) {
+ json_write_string(out, "WChan");
+ out->write(out, ':');
+ json_write_ulong(out, wchan);
+ out->write(out, ',');
+ }
+
+ json_write_string(out, "NSwap");
+ out->write(out, ':');
+ json_write_ulong(out, nswap);
+ out->write(out, ',');
+
+ json_write_string(out, "CNSwap");
+ out->write(out, ':');
+ json_write_ulong(out, cnswap);
+ out->write(out, ',');
+
+ json_write_string(out, "ExitSignal");
+ out->write(out, ':');
+ json_write_long(out, exit_signal);
+ out->write(out, ',');
+
+ json_write_string(out, "Processor");
+ out->write(out, ':');
+ json_write_long(out, processor);
+ out->write(out, ',');
+
+ json_write_string(out, "RTPriority");
+ out->write(out, ':');
+ json_write_ulong(out, rt_priority);
+ out->write(out, ',');
+
+ json_write_string(out, "Policy");
+ out->write(out, ':');
+ json_write_ulong(out, policy);
+ out->write(out, ',');
+ }
+ close(f);
+ }
+ }
+
+ if (parent_id != NULL && parent_id[0] != 0) {
+ json_write_string(out, "ParentID");
+ out->write(out, ':');
+ json_write_string(out, parent_id);
+ out->write(out, ',');
+ }
+
+ json_write_string(out, "ID");
+ out->write(out, ':');
+ json_write_string(out, id);
+
+ out->write(out, '}');
+}
+
+static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ pid_t pid = 0;
+ pid_t parent = 0;
+ int err = 0;
+ char dir[FILE_PATH_SIZE];
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ pid = id2pid(id, &parent);
+ if (pid != 0) {
+ struct_stat st;
+ if (parent != 0) {
+ snprintf(dir, sizeof(dir), "/proc/%d/task/%d", parent, pid);
+ }
+ else {
+ snprintf(dir, sizeof(dir), "/proc/%d", pid);
+ }
+ if (lstat(dir, &st) < 0) err = errno;
+ else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
+ }
+
+ write_errno(out, err);
+
+ if (err == 0 && pid != 0) {
+ char bf[256];
+ write_context(out, id, parent == 0 ? NULL : strcpy(bf, pid2id(parent, 0)), dir);
+ out->write(out, 0);
+ }
+ else {
+ write_stringz(out, "null");
+ }
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ DIR * proc = NULL;
+ char dir[FILE_PATH_SIZE];
+ pid_t pid = 0;
+ pid_t parent = 0;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ pid = id2pid(id, &parent);
+ if (pid == 0) strcpy(dir, "/proc");
+ else snprintf(dir, sizeof(dir), "/proc/%d/task", pid);
+
+ if (parent != 0) {
+ write_errno(out, 0);
+ write_stringz(out, "null");
+ }
+ else {
+ proc = opendir(dir);
+ if (proc == NULL) {
+ write_errno(out, errno);
+ write_stringz(out, "null");
+ }
+ else {
+ int cnt = 0;
+ write_errno(out, 0);
+ out->write(out, '[');
+ for (;;) {
+ struct dirent * ent = readdir(proc);
+ if (ent == NULL) break;
+ if (ent->d_name[0] >= '1' && ent->d_name[0] <= '9') {
+ if (cnt > 0) out->write(out, ',');
+ json_write_string(out, pid2id(atol(ent->d_name), pid));
+ cnt++;
+ }
+ }
+ out->write(out, ']');
+ out->write(out, 0);
+ closedir(proc);
+ }
+ }
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_command_line(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ pid_t pid = 0;
+ pid_t parent = 0;
+ int err = 0;
+ char dir[256];
+ int f;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ pid = id2pid(id, &parent);
+ if (pid != 0 && parent == 0) {
+ struct_stat st;
+ snprintf(dir, sizeof(dir), "/proc/%d", pid);
+ if (lstat(dir, &st) < 0) err = errno;
+ else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
+ }
+ else {
+ err = ERR_INV_CONTEXT;
+ }
+
+ if (err == 0 && chdir(dir) < 0) err = errno;
+ if (err == 0 && (f = open("cmdline", O_RDONLY)) < 0) err = errno;
+
+ write_errno(out, err);
+
+ if (err == 0) {
+ write_string_array(out, f);
+ close(f);
+ out->write(out, 0);
+ }
+ else {
+ write_stringz(out, "null");
+ }
+
+ out->write(out, MARKER_EOM);
+}
+
+static void command_get_environment(char * token, InputStream * inp, OutputStream * out) {
+ char id[256];
+ pid_t pid = 0;
+ pid_t parent = 0;
+ int err = 0;
+ char dir[256];
+ int f;
+
+ json_read_string(inp, id, sizeof(id));
+ if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ pid = id2pid(id, &parent);
+ if (pid != 0 && parent == 0) {
+ struct_stat st;
+ snprintf(dir, sizeof(dir), "/proc/%d", pid);
+ if (lstat(dir, &st) < 0) err = errno;
+ else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
+ }
+ else {
+ err = ERR_INV_CONTEXT;
+ }
+
+ if (err == 0 && chdir(dir) < 0) err = errno;
+ if (err == 0 && (f = open("environ", O_RDONLY)) < 0) err = errno;
+
+ write_errno(out, err);
+
+ if (err == 0) {
+ write_string_array(out, f);
+ close(f);
+ out->write(out, 0);
+ }
+ else {
+ write_stringz(out, "null");
+ }
+
+ out->write(out, MARKER_EOM);
+}
+
+extern void ini_sys_mon_service(void) {
+ add_command_handler(SYS_MON, "getContext", command_get_context);
+ add_command_handler(SYS_MON, "getChildren", command_get_children);
+ add_command_handler(SYS_MON, "getCommandLine", command_get_command_line);
+ add_command_handler(SYS_MON, "getEnvironment", command_get_environment);
+}
+
+#endif
+
diff --git a/sysmon.h b/sysmon.h
new file mode 100644
index 00000000..ef40aef3
--- /dev/null
+++ b/sysmon.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Target service implementation: system monitor (TCF name SysMonitor)
+ */
+
+#ifndef D_sysmon
+#define D_sysmon
+
+/*
+ * Initialize system monitor service.
+ */
+extern void ini_sys_mon_service(void);
+
+#endif
diff --git a/tcf.h b/tcf.h
new file mode 100644
index 00000000..034ea3e3
--- /dev/null
+++ b/tcf.h
@@ -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
+ *******************************************************************************/
+
+#ifndef D_tcf
+#define D_tcf
+
+#define PKT_SIZE 0x1000
+
+#define UDP_REQ_INFO 1
+#define UDP_ACK_INFO 2
+
+#endif
diff --git a/test.c b/test.c
new file mode 100644
index 00000000..1289ba2f
--- /dev/null
+++ b/test.c
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Agent self-testing service.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <assert.h>
+#include "myalloc.h"
+#include "mdep.h"
+#include "test.h"
+#include "trace.h"
+#include "context.h"
+
+void tcf_test_func2(void) {
+ usleep(1000);
+}
+
+void tcf_test_func1(void) {
+ tcf_test_func2();
+}
+
+void tcf_test_func0(void) {
+ tcf_test_func1();
+}
+
+char * tcf_test_array = NULL;
+
+static void * test_sub(void * x) {
+ volatile int * test_done = (int *)x;
+ while (!*test_done) {
+ tcf_test_func0();
+ }
+ return NULL;
+}
+
+static void test_proc(void) {
+ int i;
+ pthread_t thread[4];
+ int test_done = 0;
+ for (i = 0; i < 4; i++) {
+ thread[i] = 0;
+ }
+ for (i = 0; i < 4; i++) {
+ if (pthread_create(thread + i, &pthread_create_attr, test_sub, &test_done) != 0) {
+ perror("pthread_create");
+ break;
+ }
+ }
+ for (i = 0; i < 10; i++) {
+ tcf_test_func0();
+ }
+ test_done = 1;
+ for (i = 0; i < 4; i++) {
+ if (thread[i]) pthread_join(thread[i], NULL);
+ }
+}
+
+int run_test_process(pid_t * res) {
+#if defined(WIN32)
+ errno = EINVAL;
+ return -1;
+#elif defined(_WRS_KERNEL)
+ int tid = taskCreate("tTcf", 100, 0, 0x4000, (FUNCPTR)test_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ if (tid == 0) return -1;
+ taskStop(tid);
+ taskActivate(tid);
+ assert(taskIsStopped(tid));
+ if (tcf_test_array == NULL) tcf_test_array = loc_alloc(0x1000);
+ if (res != NULL) *res = tid;
+ return context_attach(tid, NULL);
+#else
+ /* Create child process to debug */
+ Context * ctx = NULL;
+ int pid = fork();
+ if (pid < 0) return -1;
+ if (pid == 0) {
+ tcf_test_array = loc_alloc(0x1000);
+ tkill(getpid(), SIGSTOP);
+ test_proc();
+ exit(0);
+ }
+ if (res != NULL) *res = pid;
+ if (context_attach(pid, &ctx) < 0) return -1;
+ ctx->pending_intercept = 1;
+ return 0;
+#endif
+}
+
diff --git a/test.h b/test.h
new file mode 100644
index 00000000..6baec28f
--- /dev/null
+++ b/test.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Agent self-testing service.
+ */
+
+#ifndef D_test
+#define D_test
+
+#include "context.h"
+
+extern int run_test_process(pid_t * pid);
+
+#endif
diff --git a/trace.c b/trace.c
new file mode 100644
index 00000000..dffa79ec
--- /dev/null
+++ b/trace.c
@@ -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
+ *******************************************************************************/
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include "mdep.h"
+#include "trace.h"
+
+FILE * log_file = NULL;
+int log_mode = LOG_EVENTS | LOG_CHILD | LOG_WAITPID | LOG_CONTEXT | LOG_PROTOCOL;
+
+static pthread_mutex_t mutex;
+
+int print_trace(int mode, char *fmt, ...) {
+ va_list ap;
+ struct timespec timenow;
+ char tmpbuf[1000];
+
+ if (log_file == NULL || mode != LOG_ALWAYS && (log_mode & mode) == 0) {
+ return 0;
+ }
+
+ if (clock_gettime(CLOCK_REALTIME, &timenow)) {
+ perror("clock_gettime");
+ exit(1);
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap);
+ va_end(ap);
+
+ pthread_mutex_lock(&mutex);
+
+ fprintf(log_file, "TCF %02d%02d.%03d: %s\n",
+ timenow.tv_sec / 60 % 60,
+ timenow.tv_sec % 60,
+ timenow.tv_nsec / 1000000,
+ tmpbuf);
+ fflush(log_file);
+
+ pthread_mutex_unlock(&mutex);
+
+ return 1;
+}
+
+void ini_trace(void) {
+ pthread_mutex_init(&mutex, NULL);
+}
+
+
diff --git a/trace.h b/trace.h
new file mode 100644
index 00000000..290b5f77
--- /dev/null
+++ b/trace.h
@@ -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
+ *******************************************************************************/
+
+/*
+ * Log file and tracing.
+ */
+
+#ifndef D_trace
+#define D_trace
+
+#include <stdio.h>
+
+#define LOG_ALWAYS 0x0
+#define LOG_ALLOC 0x1
+#define LOG_EVENTCORE 0x2
+#define LOG_WAITPID 0x4
+#define LOG_EVENTS 0x8
+#define LOG_CHILD 0x10
+#define LOG_PROTOCOL 0x20
+#define LOG_CONTEXT 0x40
+
+extern FILE * log_file;
+extern int log_mode;
+
+/*
+ * Print a trace message into log file.
+ * Use macro 'trace' intead of calling this function directly.
+ */
+extern int print_trace(int mode, char *fmt, ...);
+
+#define trace log_file && print_trace
+
+extern void ini_trace(void);
+
+#endif

Back to the top