[PATCH 2/4] covoar: Use DWARF to map addresses to source files and lines.

Vijay Kumar Banerjee vijaykumar9597 at gmail.com
Sat May 5 16:20:12 UTC 2018


In file included from ../rtemstoolkit/rld-dwarf.h:29:0,
                 from ../tester/covoar/ExecutableInfo.h:14,
                 from ../tester/covoar/DesiredSymbols.h:17,
                 from ../tester/covoar/app_common.h:11,
                 from ../tester/covoar/app_common.cc:40:
../rtemstoolkit/rld-dwarf-types.h:28:10: fatal error: dwarf.h: No such file
or directory
 #include <dwarf.h>
          ^~~~~~~~~
compilation terminated.

In file included from ../rtemstoolkit/rld-dwarf.h:29:0,
                 from ../tester/covoar/ExecutableInfo.h:14,
                 from ../tester/covoar/CoverageReaderBase.h:10,
                 from ../tester/covoar/CoverageFactory.h:12,
                 from ../tester/covoar/CoverageFactory.cc:13:
../rtemstoolkit/rld-dwarf-types.h:28:10: fatal error: dwarf.h: No such file
or directory
 #include <dwarf.h>
          ^~~~~~~~~
compilation terminated.

In file included from ../rtemstoolkit/rld-dwarf.h:29:0,
                 from ../tester/covoar/ExecutableInfo.h:14,
                 from ../tester/covoar/CoverageReaderBase.h:10,
                 from ../tester/covoar/CoverageReaderBase.cc:8:
../rtemstoolkit/rld-dwarf-types.h:28:10: fatal error: dwarf.h: No such file
or directory
 #include <dwarf.h>
          ^~~~~~~~~
compilation terminated.

In file included from ../rtemstoolkit/rld-dwarf.h:29:0,
                 from ../tester/covoar/ExecutableInfo.h:14,
                 from ../tester/covoar/DesiredSymbols.h:17,
                 from ../tester/covoar/app_common.h:11,
                 from ../tester/covoar/CoverageReaderQEMU.cc:14:
../rtemstoolkit/rld-dwarf-types.h:28:10: fatal error: dwarf.h: No such file
or directory
 #include <dwarf.h>
          ^~~~~~~~~
compilation terminated.

Waf: Leaving directory
`/home/lunatic/development/rtems/test/rtems-tools/build'
Build failed
 -> task in 'ccovoar' failed with exit status 1:
{task 140568634826336: cxx app_common.cc -> app_common.cc.1.o}
['/usr/bin/g++', '-std=c++11', '-O2', '-g', '-Itester/covoar',
'-I../tester/covoar', '-Irtemstoolkit', '-I../rtemstoolkit',
'-Irtemstoolkit/elftoolchain/libelf',
'-I../rtemstoolkit/elftoolchain/libelf',
'-Irtemstoolkit/elftoolchain/common',
'-I../rtemstoolkit/elftoolchain/common', '-Irtemstoolkit/libiberty',
'-I../rtemstoolkit/libiberty', '../tester/covoar/app_common.cc', '-c',
'-o/home/lunatic/development/rtems/test/rtems-tools/build/tester/covoar/app_common.cc.1.o']
 -> task in 'ccovoar' failed with exit status 1:
{task 140568634826416: cxx CoverageFactory.cc -> CoverageFactory.cc.1.o}
['/usr/bin/g++', '-std=c++11', '-O2', '-g', '-Itester/covoar',
'-I../tester/covoar', '-Irtemstoolkit', '-I../rtemstoolkit',
'-Irtemstoolkit/elftoolchain/libelf',
'-I../rtemstoolkit/elftoolchain/libelf',
'-Irtemstoolkit/elftoolchain/common',
'-I../rtemstoolkit/elftoolchain/common', '-Irtemstoolkit/libiberty',
'-I../rtemstoolkit/libiberty', '../tester/covoar/CoverageFactory.cc', '-c',
'-o/home/lunatic/development/rtems/test/rtems-tools/build/tester/covoar/CoverageFactory.cc.1.o']
 -> task in 'ccovoar' failed with exit status 1:
{task 140568634392656: cxx CoverageReaderBase.cc ->
CoverageReaderBase.cc.1.o}
['/usr/bin/g++', '-std=c++11', '-O2', '-g', '-Itester/covoar',
'-I../tester/covoar', '-Irtemstoolkit', '-I../rtemstoolkit',
'-Irtemstoolkit/elftoolchain/libelf',
'-I../rtemstoolkit/elftoolchain/libelf',
'-Irtemstoolkit/elftoolchain/common',
'-I../rtemstoolkit/elftoolchain/common', '-Irtemstoolkit/libiberty',
'-I../rtemstoolkit/libiberty', '../tester/covoar/CoverageReaderBase.cc',
'-c',
'-o/home/lunatic/development/rtems/test/rtems-tools/build/tester/covoar/CoverageReaderBase.cc.1.o']
 -> task in 'ccovoar' failed with exit status 1:
{task 140568634392736: cxx CoverageReaderQEMU.cc ->
CoverageReaderQEMU.cc.1.o}
['/usr/bin/g++', '-std=c++11', '-O2', '-g', '-Itester/covoar',
'-I../tester/covoar', '-Irtemstoolkit', '-I../rtemstoolkit',
'-Irtemstoolkit/elftoolchain/libelf',
'-I../rtemstoolkit/elftoolchain/libelf',
'-Irtemstoolkit/elftoolchain/common',
'-I../rtemstoolkit/elftoolchain/common', '-Irtemstoolkit/libiberty',
'-I../rtemstoolkit/libiberty', '../tester/covoar/CoverageReaderQEMU.cc',
'-c',
'-o/home/lunatic/development/rtems/test/rtems-tools/build/tester/covoar/CoverageReaderQEMU.cc.1.o']

On 5 May 2018 at 21:47, Joel Sherrill <joel at rtems.org> wrote:

>
>
> On Sat, May 5, 2018, 10:57 AM Vijay Kumar Banerjee <
> vijaykumar9597 at gmail.com> wrote:
>
>> Hello,
>>
>> I'm getting this build failure after applying this patch
>>
>> ---
>> Build failed
>>  -> task in 'ccovoar' failed with exit status 1:
>> {task 140086064254560: cxx app_common.cc -> app_common.cc.1.o}
>> ['/usr/bin/g++', '-std=c++11', '-O2', '-g', '-Itester/covoar',
>> '-I../tester/covoar', '-Irtemstoolkit', '-I../rtemstoolkit',
>> '-Irtemstoolkit/elftoolchain/libelf', '-I../rtemstoolkit/elftoolchain/libelf',
>> '-Irtemstoolkit/elftoolchain/common', '-I../rtemstoolkit/elftoolchain/common',
>> '-Irtemstoolkit/libiberty', '-I../rtemstoolkit/libiberty',
>> '../tester/covoar/app_common.cc', '-c', '-o/home/lunatic/development/
>> rtems/test/rtems-tools/build/tester/covoar/app_common.cc.1.o']
>>
>
> I don't see an error. Just the command. Does waf -v show more? Were there
> error messages after the command?
>

>>
>> -- vijay
>>
>> On 5 May 2018 at 14:10, Chris Johns <chrisj at rtems.org> wrote:
>>
>>> ---
>>>  tester/covoar/DesiredSymbols.cc | 122 ++----------------------------
>>> ----------
>>>  tester/covoar/ExecutableInfo.cc |  80 +++++++++++++++++---------
>>>  tester/covoar/ExecutableInfo.h  |  41 +++++++++++---
>>>  tester/covoar/covoar.cc         |   2 +-
>>>  tester/covoar/wscript           |   2 +-
>>>  5 files changed, 94 insertions(+), 153 deletions(-)
>>>
>>> diff --git a/tester/covoar/DesiredSymbols.cc b/tester/covoar/
>>> DesiredSymbols.cc
>>> index 9957b28..79ee78d 100644
>>> --- a/tester/covoar/DesiredSymbols.cc
>>> +++ b/tester/covoar/DesiredSymbols.cc
>>> @@ -449,123 +449,13 @@ namespace Coverage {
>>>
>>>    )
>>>    {
>>> -    char*                              base;
>>> -    char*                              cStatus;
>>> -    char                               command[512];
>>> -    std::string                        fileName;
>>> -    CoverageRanges::ranges_t::iterator ritr;
>>> -    char                               rpath[PATH_MAX];
>>> -    FILE*                              tmpfile;
>>> -
>>> -    // Open a temporary file for the uncovered ranges.
>>> -    tmpfile = fopen( "ranges1.tmp", "w" );
>>> -    if ( !tmpfile ) {
>>> -      fprintf(
>>> -        stderr,
>>> -        "ERROR: DesiredSymbols::determineSourceLines - "
>>> -        "unable to open %s\n",
>>> -        "ranges1.tmp"
>>> -      );
>>> -      exit(-1);
>>> -    }
>>> -
>>> -    // Write the range addresses to the temporary file.
>>> -    for (ritr =  theRanges->set.begin();
>>> -         ritr != theRanges->set.end();
>>> -         ritr++ ) {
>>> -      fprintf(
>>> -        tmpfile,
>>> -        "0x%08x\n0x%08x\n",
>>> -        ritr->lowAddress - theExecutable->getLoadAddress(),
>>> -        ritr->highAddress - theExecutable->getLoadAddress()
>>> -      );
>>> -    }
>>> -
>>> -    fclose( tmpfile );
>>> -
>>> -    // Invoke addr2line to generate the source lines for each address.
>>> -    if (theExecutable->hasDynamicLibrary())
>>> -      fileName = theExecutable->getLibraryName();
>>> -    else
>>> -      fileName = theExecutable->getFileName();
>>> -
>>> -    sprintf(
>>> -      command,
>>> -      "%s -Ce %s <%s | dos2unix >%s",
>>> -      TargetInfo->getAddr2line(),
>>> -      fileName.c_str(),
>>> -      "ranges1.tmp",
>>> -      "ranges2.tmp"
>>> -    );
>>> -
>>> -    if (system( command )) {
>>> -      fprintf(
>>> -        stderr,
>>> -        "ERROR: DesiredSymbols::determineSourceLines - "
>>> -        "command (%s) failed\n",
>>> -        command
>>> -      );
>>> -      exit( -1 );
>>> -    }
>>> -
>>> -    // Open the addr2line output file.
>>> -    tmpfile = fopen( "ranges2.tmp", "r" );
>>> -    if ( !tmpfile ) {
>>> -      fprintf(
>>> -        stderr,
>>> -        "ERROR: DesiredSymbols::determineSourceLines - "
>>> -        "unable to open %s\n",
>>> -        "ranges2.tmp"
>>> -      );
>>> -      exit(-1);
>>> +    for (auto& r : theRanges->set) {
>>> +      std::string location;
>>> +      theExecutable->getSourceAndLine(r.lowAddress, location);
>>> +      r.lowSourceLine = rld::path::basename (location);
>>> +      theExecutable->getSourceAndLine(r.highAddress, location);
>>> +      r.highSourceLine = rld::path::basename (location);
>>>      }
>>> -
>>> -    // Process the addr2line output.
>>> -    for (ritr =  theRanges->set.begin();
>>> -         ritr != theRanges->set.end();
>>> -         ritr++ ) {
>>> -
>>> -      cStatus = fgets( inputBuffer, MAX_LINE_LENGTH, tmpfile );
>>> -      if ( cStatus == NULL ) {
>>> -        fprintf(
>>> -          stderr,
>>> -          "ERROR: DesiredSymbols::determineSourceLines - "
>>> -          "Out of sync in addr2line output\n"
>>> -        );
>>> -        exit( -1 );
>>> -      }
>>> -      inputBuffer[ strlen(inputBuffer) - 1] = '\0';
>>> -
>>> -      // Use only the base filename without directory path.
>>> -#ifdef _WIN32
>>> -      #define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
>>> -#endif
>>> -      realpath( inputBuffer, rpath );
>>> -      base = basename( rpath );
>>> -
>>> -      ritr->lowSourceLine = std::string( base );
>>> -
>>> -      cStatus = fgets( inputBuffer, MAX_LINE_LENGTH, tmpfile );
>>> -      if ( cStatus == NULL ) {
>>> -        fprintf(
>>> -          stderr,
>>> -          "ERROR: DesiredSymbols::determineSourceLines - "
>>> -          "Out of sync in addr2line output\n"
>>> -        );
>>> -        exit( -1 );
>>> -      }
>>> -      inputBuffer[ strlen(inputBuffer) - 1] = '\0';
>>> -
>>> -      // Use only the base filename without directory path.
>>> -      realpath( inputBuffer, rpath );
>>> -      base = basename( rpath );
>>> -
>>> -      ritr->highSourceLine = std::string( base );
>>> -    }
>>> -
>>> -    fclose( tmpfile );
>>> -    unlink( "ranges1.tmp" );
>>> -    unlink( "ranges2.tmp" );
>>>    }
>>>
>>>    SymbolInformation* DesiredSymbols::find(
>>> diff --git a/tester/covoar/ExecutableInfo.cc b/tester/covoar/
>>> ExecutableInfo.cc
>>> index c41d931..1755e93 100644
>>> --- a/tester/covoar/ExecutableInfo.cc
>>> +++ b/tester/covoar/ExecutableInfo.cc
>>> @@ -7,6 +7,8 @@
>>>
>>>  #include <stdio.h>
>>>
>>> +#include <rld.h>
>>> +
>>>  #include "ExecutableInfo.h"
>>>  #include "app_common.h"
>>>  #include "CoverageMap.h"
>>> @@ -18,24 +20,36 @@ namespace Coverage {
>>>    ExecutableInfo::ExecutableInfo(
>>>      const char* const theExecutableName,
>>>      const char* const theLibraryName
>>> -  )
>>> +    ) : executable(theExecutableName),
>>> +        loadAddress(0)
>>>    {
>>> -    executableName = theExecutableName;
>>> -    loadAddress = 0;
>>> -    libraryName = "";
>>>      if (theLibraryName)
>>>        libraryName = theLibraryName;
>>> -    theSymbolTable = new SymbolTable();
>>> +    try {
>>> +      executable.open();
>>> +      executable.begin();
>>> +      executable.load_symbols(symbols);
>>> +      debug.begin(executable.elf());
>>> +      debug.load_debug();
>>> +    } catch (rld::error re) {
>>> +      std::cerr << "error: "
>>> +                << re.where << ": " << re.what
>>> +                << std::endl;
>>> +      exit(2);
>>> +    } catch (...) {
>>> +      exit(2);
>>> +    }
>>>    }
>>>
>>>    ExecutableInfo::~ExecutableInfo()
>>>    {
>>> -    if (theSymbolTable)
>>> -      delete theSymbolTable;
>>> +    debug.end();
>>> +    executable.end();
>>> +    executable.close();
>>>    }
>>>
>>>    void ExecutableInfo::dumpCoverageMaps( void ) {
>>> -    ExecutableInfo::coverageMaps_t::iterator  itr;
>>> +    ExecutableInfo::CoverageMaps::iterator  itr;
>>>
>>>      for (itr = coverageMaps.begin(); itr != coverageMaps.end(); itr++) {
>>>        fprintf( stderr, "Coverage Map for %s\n", ((*itr).first).c_str()
>>> );;
>>> @@ -44,21 +58,22 @@ namespace Coverage {
>>>    }
>>>
>>>    void ExecutableInfo::dumpExecutableInfo( void ){
>>> -    fprintf( stdout, "\n== Executable info ==\n");
>>> -    fprintf( stdout, "executableName = %s\n", executableName.c_str());
>>> -    fprintf( stdout, "libraryName = %s\n", libraryName.c_str());
>>> -    fprintf( stdout, "loadAddress = %u\n", loadAddress);
>>> -    theSymbolTable->dumpSymbolTable();
>>> +    std::cout << std::endl
>>> +              << "== Executable info ==" << std::endl
>>> +              << "executable = " << getFileName () << std::endl
>>> +              << "library = " << libraryName << std::endl
>>> +              << "loadAddress = " << loadAddress << std::endl;
>>> +    theSymbolTable.dumpSymbolTable();
>>>    }
>>>
>>>    CoverageMapBase* ExecutableInfo::getCoverageMap ( uint32_t address )
>>>    {
>>> -    CoverageMapBase*         aCoverageMap = NULL;
>>> -    coverageMaps_t::iterator it;
>>> -    std::string              itsSymbol;
>>> +    CoverageMapBase*       aCoverageMap = NULL;
>>> +    CoverageMaps::iterator it;
>>> +    std::string            itsSymbol;
>>>
>>>      // Obtain the coverage map containing the specified address.
>>> -    itsSymbol = theSymbolTable->getSymbol( address );
>>> +    itsSymbol = theSymbolTable.getSymbol( address );
>>>      if (itsSymbol != "") {
>>>        it = coverageMaps.find( itsSymbol );
>>>        aCoverageMap = (*it).second;
>>> @@ -67,12 +82,12 @@ namespace Coverage {
>>>      return aCoverageMap;
>>>    }
>>>
>>> -  const std::string& ExecutableInfo::getFileName ( void ) const
>>> +  const std::string ExecutableInfo::getFileName ( void ) const
>>>    {
>>> -    return executableName;
>>> +    return executable.name().full();
>>>    }
>>>
>>> -  const std::string& ExecutableInfo::getLibraryName( void ) const
>>> +  const std::string ExecutableInfo::getLibraryName( void ) const
>>>    {
>>>      return libraryName;
>>>    }
>>> @@ -83,9 +98,9 @@ namespace Coverage {
>>>    }
>>>
>>>
>>> -  SymbolTable* ExecutableInfo::getSymbolTable ( void ) const
>>> +  SymbolTable* ExecutableInfo::getSymbolTable ( void )
>>>    {
>>> -    return theSymbolTable;
>>> +    return &theSymbolTable;
>>>    }
>>>
>>>    CoverageMapBase* ExecutableInfo::createCoverageMap (
>>> @@ -95,8 +110,8 @@ namespace Coverage {
>>>      uint32_t           highAddress
>>>    )
>>>    {
>>> -    CoverageMapBase                          *theMap;
>>> -    ExecutableInfo::coverageMaps_t::iterator  itr;
>>> +    CoverageMapBase                        *theMap;
>>> +    ExecutableInfo::CoverageMaps::iterator  itr;
>>>
>>>      itr = coverageMaps.find( symbolName );
>>>      if ( itr == coverageMaps.end() ) {
>>> @@ -109,13 +124,26 @@ namespace Coverage {
>>>      return theMap;
>>>    }
>>>
>>> +  void ExecutableInfo::getSourceAndLine(
>>> +    const unsigned int address,
>>> +    std::string&       line
>>> +  )
>>> +  {
>>> +    std::string file;
>>> +    int         lno;
>>> +    debug.get_source (address, file, lno);
>>> +    std::ostringstream ss;
>>> +    ss << file << ':' << lno;
>>> +    line = ss.str ();
>>> +  }
>>> +
>>>    bool ExecutableInfo::hasDynamicLibrary( void )
>>>    {
>>> -     return (libraryName != "");
>>> +    return !libraryName.empty();
>>>    }
>>>
>>>    void ExecutableInfo::mergeCoverage( void ) {
>>> -    ExecutableInfo::coverageMaps_t::iterator  itr;
>>> +    ExecutableInfo::CoverageMaps::iterator  itr;
>>>
>>>      for (itr = coverageMaps.begin(); itr != coverageMaps.end(); itr++) {
>>>        SymbolsToAnalyze->mergeCoverageMap( (*itr).first, (*itr).second
>>> );
>>> diff --git a/tester/covoar/ExecutableInfo.h b/tester/covoar/
>>> ExecutableInfo.h
>>> index 20ea9bf..9106db3 100644
>>> --- a/tester/covoar/ExecutableInfo.h
>>> +++ b/tester/covoar/ExecutableInfo.h
>>> @@ -11,6 +11,10 @@
>>>  #include <stdint.h>
>>>  #include <string>
>>>
>>> +#include <rld-dwarf.h>
>>> +#include <rld-files.h>
>>> +#include <rld-symbols.h>
>>> +
>>>  #include "CoverageMapBase.h"
>>>  #include "SymbolTable.h"
>>>
>>> @@ -67,14 +71,14 @@ namespace Coverage {
>>>       *
>>>       *  @return Returns the executable's file name
>>>       */
>>> -    const std::string& getFileName( void ) const;
>>> +    const std::string getFileName( void ) const;
>>>
>>>      /*!
>>>       *  This method returns the library name associated with the
>>> executable.
>>>       *
>>>       *  @return Returns the executable's library name
>>>       */
>>> -    const std::string& getLibraryName( void ) const;
>>> +    const std::string getLibraryName( void ) const;
>>>
>>>      /*!
>>>       *  This method returns the load address of the dynamic library
>>> @@ -88,7 +92,7 @@ namespace Coverage {
>>>       *
>>>       *  @return Returns a pointer to the symbol table.
>>>       */
>>> -    SymbolTable* getSymbolTable( void ) const;
>>> +    SymbolTable* getSymbolTable( void );
>>>
>>>      /*!
>>>       *  This method creates a coverage map for the specified symbol.
>>> @@ -107,6 +111,15 @@ namespace Coverage {
>>>        uint32_t           highAddress
>>>      );
>>>
>>> +    /*!
>>> +     *  This method gets the source location, the file and line number
>>> given an
>>> +     *  address.
>>> +     */
>>> +    void getSourceAndLine(
>>> +      const unsigned int address,
>>> +      std::string&       location
>>> +    );
>>> +
>>>      /*!
>>>       *  This method indicates whether a dynamic library has been
>>>       *  associated with the executable.
>>> @@ -132,15 +145,25 @@ namespace Coverage {
>>>    private:
>>>
>>>      /*!
>>> -     *  This map associates a symbol with its coverage map.
>>> +     *  The ELF executable.
>>>       */
>>> -    typedef std::map<std::string, CoverageMapBase *> coverageMaps_t;
>>> -    coverageMaps_t coverageMaps;
>>> +    rld::files::object executable;
>>>
>>>      /*!
>>> -     *  This member variable contains the name of the executable.
>>> +     *  The DWARF data to the ELF executable.
>>> +     */
>>> +    rld::dwarf::file debug;
>>> +
>>> +    /*!
>>> +     *  The executable's symbol table.
>>> +     */
>>> +    rld::symbols::table symbols;
>>> +
>>> +    /*!
>>> +     *  This map associates a symbol with its coverage map.
>>>       */
>>> -    std::string executableName;
>>> +    typedef std::map<std::string, CoverageMapBase *> CoverageMaps;
>>> +    CoverageMaps coverageMaps;
>>>
>>>      /*!
>>>       *  This member variable contains the name of a dynamic library
>>> @@ -158,7 +181,7 @@ namespace Coverage {
>>>       *  This member variable contains a pointer to the symbol table
>>>       *  of the executable or library.
>>>       */
>>> -    SymbolTable* theSymbolTable;
>>> +    SymbolTable theSymbolTable;
>>>
>>>    };
>>>  }
>>> diff --git a/tester/covoar/covoar.cc b/tester/covoar/covoar.cc
>>> index 5c87402..09c86c2 100644
>>> --- a/tester/covoar/covoar.cc
>>> +++ b/tester/covoar/covoar.cc
>>> @@ -186,7 +186,7 @@ int main(
>>>    Coverage::ExecutableInfo*     executableInfo = NULL;
>>>    std::string                   executableExtension = "exe";
>>>    std::string                   coverageExtension = "cov";
>>> -  Coverage::CoverageFormats_t   coverageFormat;
>>> +  Coverage::CoverageFormats_t   coverageFormat =
>>> Coverage::COVERAGE_FORMAT_QEMU;
>>>    Coverage::CoverageReaderBase* coverageReader = NULL;
>>>    char*                         executable = NULL;
>>>    const char*                   explanations = NULL;
>>> diff --git a/tester/covoar/wscript b/tester/covoar/wscript
>>> index c0270d8..55d5ec9 100644
>>> --- a/tester/covoar/wscript
>>> +++ b/tester/covoar/wscript
>>> @@ -70,7 +70,7 @@ def build(bld):
>>>      #
>>>      # The list of modules.
>>>      #
>>> -    modules = ['rld', 'elf', 'iberty']
>>> +    modules = ['rld', 'dwarf', 'elf', 'iberty']
>>>
>>>      bld.stlib(target = 'ccovoar',
>>>                source = ['app_common.cc',
>>> --
>>> 2.15.1
>>>
>>> _______________________________________________
>>> devel mailing list
>>> devel at rtems.org
>>> http://lists.rtems.org/mailman/listinfo/devel
>>>
>>
>> _______________________________________________
>> devel mailing list
>> devel at rtems.org
>> http://lists.rtems.org/mailman/listinfo/devel
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/devel/attachments/20180505/fe5b8de6/attachment-0002.html>


More information about the devel mailing list