diff options
-rwxr-xr-x | .cproject | 1126 | ||||
-rwxr-xr-x | .project | 81 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | Makefile_cygwin.mak | 16 | ||||
-rw-r--r-- | TODO.txt | 80 | ||||
-rw-r--r-- | agent.dsp | 341 | ||||
-rw-r--r-- | agent.dsw | 29 | ||||
-rw-r--r-- | base64.c | 112 | ||||
-rw-r--r-- | base64.h | 35 | ||||
-rw-r--r-- | breakpoints.c | 964 | ||||
-rw-r--r-- | breakpoints.h | 47 | ||||
-rw-r--r-- | channel.c | 763 | ||||
-rw-r--r-- | channel.h | 70 | ||||
-rw-r--r-- | cmdline.c | 85 | ||||
-rw-r--r-- | cmdline.h | 21 | ||||
-rw-r--r-- | config.h | 43 | ||||
-rw-r--r-- | context.c | 1159 | ||||
-rw-r--r-- | context.h | 136 | ||||
-rw-r--r-- | diagnostics.c | 185 | ||||
-rw-r--r-- | diagnostics.h | 22 | ||||
-rw-r--r-- | dwarf.h | 563 | ||||
-rw-r--r-- | dwarfio.c | 728 | ||||
-rw-r--r-- | dwarfio.h | 77 | ||||
-rw-r--r-- | elf.c | 235 | ||||
-rw-r--r-- | elf.h | 112 | ||||
-rw-r--r-- | errors.c | 58 | ||||
-rw-r--r-- | errors.h | 41 | ||||
-rw-r--r-- | events.c | 194 | ||||
-rw-r--r-- | events.h | 37 | ||||
-rw-r--r-- | exceptions.c | 84 | ||||
-rw-r--r-- | exceptions.h | 54 | ||||
-rw-r--r-- | expressions.c | 854 | ||||
-rw-r--r-- | expressions.h | 47 | ||||
-rw-r--r-- | filesystem.c | 1103 | ||||
-rw-r--r-- | filesystem.h | 24 | ||||
-rw-r--r-- | json.c | 489 | ||||
-rw-r--r-- | json.h | 50 | ||||
-rw-r--r-- | linenumbers.c | 641 | ||||
-rw-r--r-- | linenumbers.h | 27 | ||||
-rw-r--r-- | link.h | 46 | ||||
-rw-r--r-- | main.c | 173 | ||||
-rw-r--r-- | mdep.c | 621 | ||||
-rw-r--r-- | mdep.h | 278 | ||||
-rw-r--r-- | memory.c | 579 | ||||
-rw-r--r-- | memory.h | 25 | ||||
-rw-r--r-- | myalloc.c | 67 | ||||
-rw-r--r-- | myalloc.h | 26 | ||||
-rw-r--r-- | processes.c | 419 | ||||
-rw-r--r-- | processes.h | 29 | ||||
-rw-r--r-- | protocol.c | 205 | ||||
-rw-r--r-- | protocol.h | 61 | ||||
-rw-r--r-- | proxy.c | 22 | ||||
-rw-r--r-- | proxy.h | 23 | ||||
-rw-r--r-- | registers.c | 376 | ||||
-rw-r--r-- | registers.h | 25 | ||||
-rw-r--r-- | runctrl.c | 773 | ||||
-rw-r--r-- | runctrl.h | 41 | ||||
-rw-r--r-- | stacktrace.c | 425 | ||||
-rw-r--r-- | stacktrace.h | 25 | ||||
-rw-r--r-- | streams.c | 25 | ||||
-rw-r--r-- | streams.h | 44 | ||||
-rw-r--r-- | symbols.c | 231 | ||||
-rw-r--r-- | symbols.h | 34 | ||||
-rw-r--r-- | sysmon.c | 657 | ||||
-rw-r--r-- | sysmon.h | 24 | ||||
-rw-r--r-- | tcf.h | 20 | ||||
-rw-r--r-- | test.c | 101 | ||||
-rw-r--r-- | test.h | 23 | ||||
-rw-r--r-- | trace.c | 60 | ||||
-rw-r--r-- | trace.h | 43 |
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 + @@ -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 @@ -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 @@ -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); +} + @@ -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 @@ -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 @@ -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; +} @@ -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 + @@ -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 @@ -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 @@ -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 +} + @@ -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 |