<commit_before>/*
 * Copyright (c) 2007-2008 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#ifndef __BASE_BITUNION_HH__
#define __BASE_BITUNION_HH__

#include "base/bitfield.hh"
#include "base/types.hh"

//      The following implements the BitUnion system of defining bitfields
//on top of an underlying class. This is done through the pervasive use of
//both named and unnamed unions which all contain the same actual storage.
//Since they're unioned with each other, all of these storage locations
//overlap. This allows all of the bitfields to manipulate the same data
//without having to have access to each other. More details are provided with
//the individual components.

//This namespace is for classes which implement the backend of the BitUnion
//stuff. Don't use any of these directly, except for the Bitfield classes in
//the *BitfieldTypes class(es).
namespace BitfieldBackend
{
    //A base class for all bitfields. It instantiates the actual storage,
    //and provides getBits and setBits functions for manipulating it. The
    //Data template parameter is type of the underlying storage.
    template<class Data>
    class BitfieldBase
    {
      protected:
        Data __data;

        //This function returns a range of bits from the underlying storage.
        //It relies on the "bits" function above. It's the user's
        //responsibility to make sure that there is a properly overloaded
        //version of this function for whatever type they want to overlay.
        inline uint64_t
        getBits(int first, int last) const
        {
            return bits(__data, first, last);
        }

        //Similar to the above, but for settings bits with replaceBits.
        inline void
        setBits(int first, int last, uint64_t val)
        {
            replaceBits(__data, first, last, val);
        }
    };

    //This class contains all the "regular" bitfield classes. It is inherited
    //by all BitUnions which give them access to those types.
    template<class Type>
    class RegularBitfieldTypes
    {
      protected:
        //This class implements ordinary bitfields, that is a span of bits
        //who's msb is "first", and who's lsb is "last".
        template<int first, int last=first>
        class Bitfield : public BitfieldBase<Type>
        {
            static_assert(first >= last,
                          "Bitfield ranges must be specified as <msb, lsb>");

          public:
            operator const uint64_t () const
            {
                return this->getBits(first, last);
            }

            uint64_t
            operator=(const uint64_t _data)
            {
                this->setBits(first, last, _data);
                return _data;
            }
        };

        //A class which specializes the above so that it can only be read
        //from. This is accomplished explicitly making sure the assignment
        //operator is blocked. The conversion operator is carried through
        //inheritance. This will unfortunately need to be copied into each
        //bitfield type due to limitations with how templates work
        template<int first, int last=first>
        class BitfieldRO : public Bitfield<first, last>
        {
          private:
            uint64_t
            operator=(const uint64_t _data);
        };

        //Similar to the above, but only allows writing.
        template<int first, int last=first>
        class BitfieldWO : public Bitfield<first, last>
        {
          private:
            operator const uint64_t () const;

          public:
            using Bitfield<first, last>::operator=;
        };
    };

    //This class contains all the "regular" bitfield classes. It is inherited
    //by all BitUnions which give them access to those types.
    template<class Type>
    class SignedBitfieldTypes
    {
      protected:
        //This class implements ordinary bitfields, that is a span of bits
        //who's msb is "first", and who's lsb is "last".
        template<int first, int last=first>
        class SignedBitfield : public BitfieldBase<Type>
        {
          public:
            operator const int64_t () const
            {
                return sext<first - last + 1>(this->getBits(first, last));
            }

            int64_t
            operator=(const int64_t _data)
            {
                this->setBits(first, last, _data);
                return _data;
            }
        };

        //A class which specializes the above so that it can only be read
        //from. This is accomplished explicitly making sure the assignment
        //operator is blocked. The conversion operator is carried through
        //inheritance. This will unfortunately need to be copied into each
        //bitfield type due to limitations with how templates work
        template<int first, int last=first>
        class SignedBitfieldRO : public SignedBitfield<first, last>
        {
          private:
            int64_t
            operator=(const int64_t _data);
        };

        //Similar to the above, but only allows writing.
        template<int first, int last=first>
        class SignedBitfieldWO : public SignedBitfield<first, last>
        {
          private:
            operator const int64_t () const;

          public:
            int64_t operator=(const int64_t _data)
            {
                *((SignedBitfield<first, last> *)this) = _data;
                return _data;
            }
        };
    };

    template<class Type>
    class BitfieldTypes : public RegularBitfieldTypes<Type>,
                          public SignedBitfieldTypes<Type>
    {};

    //When a BitUnion is set up, an underlying class is created which holds
    //the actual union. This class then inherits from it, and provids the
    //implementations for various operators. Setting things up this way
    //prevents having to redefine these functions in every different BitUnion
    //type. More operators could be implemented in the future, as the need
    //arises.
    template <class Type, class Base>
    class BitUnionOperators : public Base
    {
      public:
        BitUnionOperators(Type const & _data)
        {
            Base::__data = _data;
        }

        BitUnionOperators() {}

        operator const Type () const
        {
            return Base::__data;
        }

        Type
        operator=(Type const & _data)
        {
            Base::__data = _data;
            return _data;
        }

        bool
        operator<(Base const & base) const
        {
            return Base::__data < base.__data;
        }

        bool
        operator==(Base const & base) const
        {
            return Base::__data == base.__data;
        }
    };
}

//This macro is a backend for other macros that specialize it slightly.
//First, it creates/extends a namespace "BitfieldUnderlyingClasses" and
//sticks the class which has the actual union in it, which
//BitfieldOperators above inherits from. Putting these classes in a special
//namespace ensures that there will be no collisions with other names as long
//as the BitUnion names themselves are all distinct and nothing else uses
//the BitfieldUnderlyingClasses namespace, which is unlikely. The class itself
//creates a typedef of the "type" parameter called __DataType. This allows
//the type to propagate outside of the macro itself in a controlled way.
//Finally, the base storage is defined which BitfieldOperators will refer to
//in the operators it defines. This macro is intended to be followed by
//bitfield definitions which will end up inside it's union. As explained
//above, these is overlayed the __data member in its entirety by each of the
//bitfields which are defined in the union, creating shared storage with no
//overhead.
#define __BitUnion(type, name) \
    class BitfieldUnderlyingClasses##name : \
        public BitfieldBackend::BitfieldTypes<type> \
    { \
      public: \
        typedef type __DataType; \
        union { \
            type __data;\

//This closes off the class and union started by the above macro. It is
//followed by a typedef which makes "name" refer to a BitfieldOperator
//class inheriting from the class and union just defined, which completes
//building up the type for the user.
#define EndBitUnion(name) \
        }; \
    }; \
    typedef BitfieldBackend::BitUnionOperators< \
        BitfieldUnderlyingClasses##name::__DataType, \
        BitfieldUnderlyingClasses##name> name;

//This sets up a bitfield which has other bitfields nested inside of it. The
//__data member functions like the "underlying storage" of the top level
//BitUnion. Like everything else, it overlays with the top level storage, so
//making it a regular bitfield type makes the entire thing function as a
//regular bitfield when referred to by itself.
#define __SubBitUnion(fieldType, first, last, name) \
    class : public BitfieldBackend::BitfieldTypes<__DataType> \
    { \
      public: \
        union { \
            fieldType<first, last> __data;

//This closes off the union created above and gives it a name. Unlike the top
//level BitUnion, we're interested in creating an object instead of a type.
//The operators are defined in the macro itself instead of a class for
//technical reasons. If someone determines a way to move them to one, please
//do so.
#define EndSubBitUnion(name) \
        }; \
        inline operator const __DataType () const \
        { return __data; } \
        \
        inline const __DataType operator = (const __DataType & _data) \
        { return __data = _data;} \
    } name;

//Regular bitfields
//These define macros for read/write regular bitfield based subbitfields.
#define SubBitUnion(name, first, last) \
    __SubBitUnion(Bitfield, first, last, name)

//Regular bitfields
//These define macros for read/write regular bitfield based subbitfields.
#define SignedSubBitUnion(name, first, last) \
    __SubBitUnion(SignedBitfield, first, last, name)

//Use this to define an arbitrary type overlayed with bitfields.
#define BitUnion(type, name) __BitUnion(type, name)

//Use this to define conveniently sized values overlayed with bitfields.
#define BitUnion64(name) __BitUnion(uint64_t, name)
#define BitUnion32(name) __BitUnion(uint32_t, name)
#define BitUnion16(name) __BitUnion(uint16_t, name)
#define BitUnion8(name) __BitUnion(uint8_t, name)

#endif // __BASE_BITUNION_HH__
<commit_msg>base: Fix assigning between identical bitfields.<commit_after>/*
 * Copyright (c) 2007-2008 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#ifndef __BASE_BITUNION_HH__
#define __BASE_BITUNION_HH__

#include "base/bitfield.hh"
#include "base/types.hh"

//      The following implements the BitUnion system of defining bitfields
//on top of an underlying class. This is done through the pervasive use of
//both named and unnamed unions which all contain the same actual storage.
//Since they're unioned with each other, all of these storage locations
//overlap. This allows all of the bitfields to manipulate the same data
//without having to have access to each other. More details are provided with
//the individual components.

//This namespace is for classes which implement the backend of the BitUnion
//stuff. Don't use any of these directly, except for the Bitfield classes in
//the *BitfieldTypes class(es).
namespace BitfieldBackend
{
    //A base class for all bitfields. It instantiates the actual storage,
    //and provides getBits and setBits functions for manipulating it. The
    //Data template parameter is type of the underlying storage.
    template<class Data>
    class BitfieldBase
    {
      protected:
        Data __data;

        //This function returns a range of bits from the underlying storage.
        //It relies on the "bits" function above. It's the user's
        //responsibility to make sure that there is a properly overloaded
        //version of this function for whatever type they want to overlay.
        inline uint64_t
        getBits(int first, int last) const
        {
            return bits(__data, first, last);
        }

        //Similar to the above, but for settings bits with replaceBits.
        inline void
        setBits(int first, int last, uint64_t val)
        {
            replaceBits(__data, first, last, val);
        }
    };

    //This class contains all the "regular" bitfield classes. It is inherited
    //by all BitUnions which give them access to those types.
    template<class Type>
    class RegularBitfieldTypes
    {
      protected:
        //This class implements ordinary bitfields, that is a span of bits
        //who's msb is "first", and who's lsb is "last".
        template<int first, int last=first>
        class Bitfield : public BitfieldBase<Type>
        {
            static_assert(first >= last,
                          "Bitfield ranges must be specified as <msb, lsb>");

          public:
            operator const uint64_t () const
            {
                return this->getBits(first, last);
            }

            uint64_t
            operator=(const uint64_t _data)
            {
                this->setBits(first, last, _data);
                return _data;
            }

            uint64_t
            operator=(Bitfield<first, last> const & other)
            {
                return *this = (uint64_t)other;
            }
        };

        //A class which specializes the above so that it can only be read
        //from. This is accomplished explicitly making sure the assignment
        //operator is blocked. The conversion operator is carried through
        //inheritance. This will unfortunately need to be copied into each
        //bitfield type due to limitations with how templates work
        template<int first, int last=first>
        class BitfieldRO : public Bitfield<first, last>
        {
          private:
            uint64_t
            operator=(const uint64_t _data);

            uint64_t
            operator=(const Bitfield<first, last>& other);
        };

        //Similar to the above, but only allows writing.
        template<int first, int last=first>
        class BitfieldWO : public Bitfield<first, last>
        {
          private:
            operator const uint64_t () const;

          public:
            using Bitfield<first, last>::operator=;
        };
    };

    //This class contains all the "regular" bitfield classes. It is inherited
    //by all BitUnions which give them access to those types.
    template<class Type>
    class SignedBitfieldTypes
    {
      protected:
        //This class implements ordinary bitfields, that is a span of bits
        //who's msb is "first", and who's lsb is "last".
        template<int first, int last=first>
        class SignedBitfield : public BitfieldBase<Type>
        {
          public:
            operator const int64_t () const
            {
                return sext<first - last + 1>(this->getBits(first, last));
            }

            int64_t
            operator=(const int64_t _data)
            {
                this->setBits(first, last, _data);
                return _data;
            }

            int64_t
            operator=(SignedBitfield<first, last> const & other)
            {
                return *this = (int64_t)other;
            }
        };

        //A class which specializes the above so that it can only be read
        //from. This is accomplished explicitly making sure the assignment
        //operator is blocked. The conversion operator is carried through
        //inheritance. This will unfortunately need to be copied into each
        //bitfield type due to limitations with how templates work
        template<int first, int last=first>
        class SignedBitfieldRO : public SignedBitfield<first, last>
        {
          private:
            int64_t
            operator=(const int64_t _data);

            int64_t
            operator=(const SignedBitfield<first, last>& other);
        };

        //Similar to the above, but only allows writing.
        template<int first, int last=first>
        class SignedBitfieldWO : public SignedBitfield<first, last>
        {
          private:
            operator const int64_t () const;

          public:
            using SignedBitfield<first, last>::operator=;
        };
    };

    template<class Type>
    class BitfieldTypes : public RegularBitfieldTypes<Type>,
                          public SignedBitfieldTypes<Type>
    {};

    //When a BitUnion is set up, an underlying class is created which holds
    //the actual union. This class then inherits from it, and provids the
    //implementations for various operators. Setting things up this way
    //prevents having to redefine these functions in every different BitUnion
    //type. More operators could be implemented in the future, as the need
    //arises.
    template <class Type, class Base>
    class BitUnionOperators : public Base
    {
      public:
        BitUnionOperators(Type const & _data)
        {
            Base::__data = _data;
        }

        BitUnionOperators() {}

        operator const Type () const
        {
            return Base::__data;
        }

        Type
        operator=(Type const & _data)
        {
            Base::__data = _data;
            return _data;
        }

        Type
        operator=(BitUnionOperators const & other)
        {
            Base::__data = other;
            return Base::__data;
        }

        bool
        operator<(Base const & base) const
        {
            return Base::__data < base.__data;
        }

        bool
        operator==(Base const & base) const
        {
            return Base::__data == base.__data;
        }
    };
}

//This macro is a backend for other macros that specialize it slightly.
//First, it creates/extends a namespace "BitfieldUnderlyingClasses" and
//sticks the class which has the actual union in it, which
//BitfieldOperators above inherits from. Putting these classes in a special
//namespace ensures that there will be no collisions with other names as long
//as the BitUnion names themselves are all distinct and nothing else uses
//the BitfieldUnderlyingClasses namespace, which is unlikely. The class itself
//creates a typedef of the "type" parameter called __DataType. This allows
//the type to propagate outside of the macro itself in a controlled way.
//Finally, the base storage is defined which BitfieldOperators will refer to
//in the operators it defines. This macro is intended to be followed by
//bitfield definitions which will end up inside it's union. As explained
//above, these is overlayed the __data member in its entirety by each of the
//bitfields which are defined in the union, creating shared storage with no
//overhead.
#define __BitUnion(type, name) \
    class BitfieldUnderlyingClasses##name : \
        public BitfieldBackend::BitfieldTypes<type> \
    { \
      public: \
        typedef type __DataType; \
        union { \
            type __data;\

//This closes off the class and union started by the above macro. It is
//followed by a typedef which makes "name" refer to a BitfieldOperator
//class inheriting from the class and union just defined, which completes
//building up the type for the user.
#define EndBitUnion(name) \
        }; \
    }; \
    typedef BitfieldBackend::BitUnionOperators< \
        BitfieldUnderlyingClasses##name::__DataType, \
        BitfieldUnderlyingClasses##name> name;

//This sets up a bitfield which has other bitfields nested inside of it. The
//__data member functions like the "underlying storage" of the top level
//BitUnion. Like everything else, it overlays with the top level storage, so
//making it a regular bitfield type makes the entire thing function as a
//regular bitfield when referred to by itself.
#define __SubBitUnion(fieldType, first, last, name) \
    class : public BitfieldBackend::BitfieldTypes<__DataType> \
    { \
      public: \
        union { \
            fieldType<first, last> __data;

//This closes off the union created above and gives it a name. Unlike the top
//level BitUnion, we're interested in creating an object instead of a type.
//The operators are defined in the macro itself instead of a class for
//technical reasons. If someone determines a way to move them to one, please
//do so.
#define EndSubBitUnion(name) \
        }; \
        inline operator const __DataType () const \
        { return __data; } \
        \
        inline const __DataType operator = (const __DataType & _data) \
        { return __data = _data;} \
    } name;

//Regular bitfields
//These define macros for read/write regular bitfield based subbitfields.
#define SubBitUnion(name, first, last) \
    __SubBitUnion(Bitfield, first, last, name)

//Regular bitfields
//These define macros for read/write regular bitfield based subbitfields.
#define SignedSubBitUnion(name, first, last) \
    __SubBitUnion(SignedBitfield, first, last, name)

//Use this to define an arbitrary type overlayed with bitfields.
#define BitUnion(type, name) __BitUnion(type, name)

//Use this to define conveniently sized values overlayed with bitfields.
#define BitUnion64(name) __BitUnion(uint64_t, name)
#define BitUnion32(name) __BitUnion(uint32_t, name)
#define BitUnion16(name) __BitUnion(uint16_t, name)
#define BitUnion8(name) __BitUnion(uint8_t, name)

#endif // __BASE_BITUNION_HH__
<|endoftext|>


<commit_before>/**
 * @file ct2ctml.cpp
 * Driver for the system call to the python executable that converts
 * cti files to ctml files (see \ref inputfiles).
 */
// Copyright 2001-2005  California Institute of Technology

#include "cantera/base/ct_defs.h"
#include "cantera/base/ctexceptions.h"
#include "cantera/base/ctml.h"
#include "cantera/base/global.h"
#include "cantera/base/stringUtils.h"

#include <fstream>
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>

// These defines are needed for the windows Sleep() function
// - comment them out if you don't want the Sleep function.
//#ifdef _WIN32
//#include "Windows.h"
//#include "Winbase.h"
//#endif

using namespace Cantera;
using namespace std;

namespace ctml
{

//! return the full path to the Python interpreter.
/*!
 * Use the environment variable PYTHON_CMD if it is set. If not, return
 * the string 'python'.
 *
 * Note, there are hidden problems here that really direct us to use
 * a full pathname for the location of python. Basically the system
 * call will use the shell /bin/sh, in order to launch python.
 * This default shell may not be the shell that the user is employing.
 * Therefore, the default path to python may be different during
 * a system call than during the default user shell environment.
 * This is quite a headache. The answer is to always set the
 * PYTHON_CMD environmental variable in the user environment to
 * an absolute path to locate the python executable. Then this
 * issue goes away.
 */
static string pypath()
{
    string s = "python";
    const char* py = getenv("PYTHON_CMD");

    if (py) {
        string sp = stripws(string(py));
        if (sp.size() > 0) {
            s = sp;
        }
    }
    return s;
}

// Convert a cti file into a ctml file
/*
 *
 *  @param   file    Pointer to the file
 *  @param   debug   Turn on debug printing
 *
 *  @ingroup inputfiles
 */
void ct2ctml(const char* file, const int debug)
{

#ifdef HAS_NO_PYTHON
    /*
     *  Section to bomb out if python is not
     *  present in the computation environment.
     */
    string ppath = file;
    throw CanteraError("ct2ctml",
                       "python cti to ctml conversion requested for file, " + ppath +
                       ", but not available in this computational environment");
#endif

    time_t aclock;
    time(&aclock);
    int ia = static_cast<int>(aclock);
    string path =  tmpDir()+"/.cttmp"+int2str(ia)+".pyw";
    ofstream f(path.c_str());
    if (!f) {
        throw CanteraError("ct2ctml","cannot open "+path+" for writing.");
    }

    f << "from ctml_writer import *\n"
      << "import sys, os, os.path\n"
      << "file = \"" << file << "\"\n"
      << "base = os.path.basename(file)\n"
      << "root, ext = os.path.splitext(base)\n"
      << "dataset(root)\n"
      << "execfile(file)\n"
      << "write()\n";
    f.close();
    string logfile = tmpDir()+"/ct2ctml.log";
#ifdef _WIN32
    string cmd = pypath() + " " + "\"" + path + "\"" + "> " + logfile + " 2>&1";
#else
    string cmd = "sleep " + sleep() + "; " + "\"" + pypath() + "\"" +
                 " " + "\"" + path + "\"" + " &> " + logfile;
#endif
#ifdef DEBUG_PATHS
    writelog("ct2ctml: executing the command " + cmd + "\n");
#endif
    if (debug > 0) {
        writelog("ct2ctml: executing the command " + cmd + "\n");
        writelog("ct2ctml: the Python command is: " + pypath() + "\n");
    }

    int ierr = 0;
    try {
        ierr = system(cmd.c_str());
    } catch (...) {
        ierr = -10;
        if (debug > 0) {
            writelog("ct2ctml: command execution failed.\n");
        }
    }

    /*
     * This next section may seem a bit weird. However, it is in
     * response to an issue that arises when running cantera with
     * cygwin, using cygwin's python intepreter. Basically, the
     * xml file is written to the local directory by the last
     * system command. Then, the xml file is read immediately
     * after by an ifstream() c++ command. Unfortunately, it seems
     * that the directory info is not being synched fast enough so
     * that the ifstream() read fails, even though the file is
     * actually there. Putting in a sleep system call here fixes
     * this problem. Also, having the xml file pre-existing fixes
     * the problem as well. There may be more direct ways to fix
     * this bug; however, I am not aware of them.
     * HKM -> During the solaris port, I found the same thing.
     *        It probably has to do with NFS syncing problems.
     *        3/3/06
     */
#ifndef _WIN32
    string sss = sleep();
    if (debug > 0) {
        writelog("sleeping for " + sss + " secs+\n");
    }
    cmd = "sleep " + sss;
    try {
        ierr = system(cmd.c_str());
    } catch (...) {
        ierr = -10;
        writelog("ct2ctml: command execution failed.\n");
    }
#else
    // This command works on windows machines if Windows.h and Winbase.h are included
    // Sleep(5000);
#endif

    if (ierr != 0) {
        // Generate an error message that includes the contents of the
        // ct2ctml log file.
        stringstream message;
        message << "Error converting input file \"" << file << "\" to CTML.\n";
        message << "Command was:\n\n";
        message << cmd << "\n\n";
        ifstream ferr(logfile.c_str());
        if (ferr) {
            message << "-------------- start of ct2ctml.log --------------\n";
            message << ferr.rdbuf();
            message << "--------------- end of ct2ctml.log ---------------";
        } else {
            message << "Additionally, the contents of ct2ctml.log"
                "could not be read";
        }
        throw CanteraError("ct2ctml", message.str());
    }

    // if the conversion succeeded and DEBUG_PATHS is not defined,
    // then clean up by deleting the temporary Python file.
#ifndef DEBUG_PATHS
    //#ifdef _WIN32
    //cmd = "cmd /C rm " + path;
    if (debug == 0) {
        remove(path.c_str());
    } else {
        writelog("ct2ctml: retaining temporary file "+path+"\n");
    }
#else
    if (debug > 0) {
        writelog("ct2ctml: retaining temporary file "+path+"\n");
    }
#endif
}

// Read an ctml file from a file and fill up an XML tree
/*
 *  This is the main routine that reads a ctml file and puts it into
 *  an XML_Node tree
 *
 *  @param node    Root of the tree
 *  @param file    Name of the file
 *  @param debug   Turn on debugging printing
 */
void get_CTML_Tree(Cantera::XML_Node* rootPtr, const std::string file, const int debug)
{

    std::string ff, ext = "";

    // find the input file on the Cantera search path
    std::string inname = findInputFile(file);
#ifdef DEBUG_PATHS
    writelog("Found file: "+inname+"\n");
#endif
    if (debug > 0) {
        writelog("Found file: "+inname+"\n");
    }

    if (inname == "") {
        throw CanteraError("get_CTML_Tree", "file "+file+" not found");
    }

    /*
     * Check whether or not the file is XML. If not, it will be first
     * processed with the preprocessor.
     */
    std::string::size_type idot = inname.rfind('.');
    if (idot != string::npos) {
        ext = inname.substr(idot, inname.size());
    }
    if (ext != ".xml" && ext != ".ctml") {
        try {
            ctml::ct2ctml(inname.c_str(), debug);
        } catch (std::exception& err) {
            writelog("get_CTML_Tree: caught an exception:\n");
            writelog(err.what());
        }
        string ffull = inname.substr(0,idot) + ".xml";
        ff = "./" + getBaseName(ffull) + ".xml";
#ifdef DEBUG_PATHS
        writelogf("ffull name = %s\n", ffull.c_str());
        writelogf("ff name = %s\n", ff.c_str());
#endif
    } else {
        ff = inname;
    }
#ifdef DEBUG_PATHS
    writelog("Attempting to parse xml file " + ff + "\n");
#else
    if (debug > 0) {
        writelog("Attempting to parse xml file " + ff + "\n");
    }
#endif
    ifstream fin(ff.c_str());
    if (!fin) {
        throw
        CanteraError("get_CTML_Tree",
                     "XML file " + ff + " not found");
    }
    rootPtr->build(fin);
    fin.close();
}
}
<commit_msg>Automatically clean up ct2ctml.log files unless there is an error<commit_after>/**
 * @file ct2ctml.cpp
 * Driver for the system call to the python executable that converts
 * cti files to ctml files (see \ref inputfiles).
 */
// Copyright 2001-2005  California Institute of Technology

#include "cantera/base/ct_defs.h"
#include "cantera/base/ctexceptions.h"
#include "cantera/base/ctml.h"
#include "cantera/base/global.h"
#include "cantera/base/stringUtils.h"

#include <fstream>
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>

// These defines are needed for the windows Sleep() function
// - comment them out if you don't want the Sleep function.
//#ifdef _WIN32
//#include "Windows.h"
//#include "Winbase.h"
//#endif

using namespace Cantera;
using namespace std;

namespace ctml
{

//! return the full path to the Python interpreter.
/*!
 * Use the environment variable PYTHON_CMD if it is set. If not, return
 * the string 'python'.
 *
 * Note, there are hidden problems here that really direct us to use
 * a full pathname for the location of python. Basically the system
 * call will use the shell /bin/sh, in order to launch python.
 * This default shell may not be the shell that the user is employing.
 * Therefore, the default path to python may be different during
 * a system call than during the default user shell environment.
 * This is quite a headache. The answer is to always set the
 * PYTHON_CMD environmental variable in the user environment to
 * an absolute path to locate the python executable. Then this
 * issue goes away.
 */
static string pypath()
{
    string s = "python";
    const char* py = getenv("PYTHON_CMD");

    if (py) {
        string sp = stripws(string(py));
        if (sp.size() > 0) {
            s = sp;
        }
    }
    return s;
}

// Convert a cti file into a ctml file
/*
 *
 *  @param   file    Pointer to the file
 *  @param   debug   Turn on debug printing
 *
 *  @ingroup inputfiles
 */
void ct2ctml(const char* file, const int debug)
{

#ifdef HAS_NO_PYTHON
    /*
     *  Section to bomb out if python is not
     *  present in the computation environment.
     */
    string ppath = file;
    throw CanteraError("ct2ctml",
                       "python cti to ctml conversion requested for file, " + ppath +
                       ", but not available in this computational environment");
#endif

    time_t aclock;
    time(&aclock);
    int ia = static_cast<int>(aclock);
    string path =  tmpDir()+"/.cttmp"+int2str(ia)+".pyw";
    ofstream f(path.c_str());
    if (!f) {
        throw CanteraError("ct2ctml","cannot open "+path+" for writing.");
    }

    f << "from ctml_writer import *\n"
      << "import sys, os, os.path\n"
      << "file = \"" << file << "\"\n"
      << "base = os.path.basename(file)\n"
      << "root, ext = os.path.splitext(base)\n"
      << "dataset(root)\n"
      << "execfile(file)\n"
      << "write()\n";
    f.close();
    string logfile = tmpDir()+"/ct2ctml.log";
#ifdef _WIN32
    string cmd = pypath() + " " + "\"" + path + "\"" + "> " + logfile + " 2>&1";
#else
    string cmd = "sleep " + sleep() + "; " + "\"" + pypath() + "\"" +
                 " " + "\"" + path + "\"" + " &> " + logfile;
#endif
#ifdef DEBUG_PATHS
    writelog("ct2ctml: executing the command " + cmd + "\n");
#endif
    if (debug > 0) {
        writelog("ct2ctml: executing the command " + cmd + "\n");
        writelog("ct2ctml: the Python command is: " + pypath() + "\n");
    }

    int ierr = 0;
    try {
        ierr = system(cmd.c_str());
    } catch (...) {
        ierr = -10;
        if (debug > 0) {
            writelog("ct2ctml: command execution failed.\n");
        }
    }

    /*
     * This next section may seem a bit weird. However, it is in
     * response to an issue that arises when running cantera with
     * cygwin, using cygwin's python intepreter. Basically, the
     * xml file is written to the local directory by the last
     * system command. Then, the xml file is read immediately
     * after by an ifstream() c++ command. Unfortunately, it seems
     * that the directory info is not being synched fast enough so
     * that the ifstream() read fails, even though the file is
     * actually there. Putting in a sleep system call here fixes
     * this problem. Also, having the xml file pre-existing fixes
     * the problem as well. There may be more direct ways to fix
     * this bug; however, I am not aware of them.
     * HKM -> During the solaris port, I found the same thing.
     *        It probably has to do with NFS syncing problems.
     *        3/3/06
     */
#ifndef _WIN32
    string sss = sleep();
    if (debug > 0) {
        writelog("sleeping for " + sss + " secs+\n");
    }
    cmd = "sleep " + sss;
    try {
        ierr = system(cmd.c_str());
    } catch (...) {
        ierr = -10;
        writelog("ct2ctml: command execution failed.\n");
    }
#else
    // This command works on windows machines if Windows.h and Winbase.h are included
    // Sleep(5000);
#endif

    if (ierr != 0) {
        // Generate an error message that includes the contents of the
        // ct2ctml log file.
        stringstream message;
        message << "Error converting input file \"" << file << "\" to CTML.\n";
        message << "Command was:\n\n";
        message << cmd << "\n\n";
        ifstream ferr(logfile.c_str());
        if (ferr) {
            message << "-------------- start of ct2ctml.log --------------\n";
            message << ferr.rdbuf();
            message << "--------------- end of ct2ctml.log ---------------";
        } else {
            message << "Additionally, the contents of ct2ctml.log"
                "could not be read";
        }
        throw CanteraError("ct2ctml", message.str());
    }

    // If the conversion succeeded and no debugging information is needed,
    // clean up by deleting the temporary Python file and the log file.
    if (debug == 0) {
        remove(path.c_str());
        remove(logfile.c_str());
    } else {
        writelog("ct2ctml: retaining temporary file "+path+"\n");
        writelog("ct2ctml: retaining temporary file "+logfile+"\n");
    }
}

// Read an ctml file from a file and fill up an XML tree
/*
 *  This is the main routine that reads a ctml file and puts it into
 *  an XML_Node tree
 *
 *  @param node    Root of the tree
 *  @param file    Name of the file
 *  @param debug   Turn on debugging printing
 */
void get_CTML_Tree(Cantera::XML_Node* rootPtr, const std::string file, const int debug)
{

    std::string ff, ext = "";

    // find the input file on the Cantera search path
    std::string inname = findInputFile(file);
#ifdef DEBUG_PATHS
    writelog("Found file: "+inname+"\n");
#endif
    if (debug > 0) {
        writelog("Found file: "+inname+"\n");
    }

    if (inname == "") {
        throw CanteraError("get_CTML_Tree", "file "+file+" not found");
    }

    /*
     * Check whether or not the file is XML. If not, it will be first
     * processed with the preprocessor.
     */
    std::string::size_type idot = inname.rfind('.');
    if (idot != string::npos) {
        ext = inname.substr(idot, inname.size());
    }
    if (ext != ".xml" && ext != ".ctml") {
        try {
            ctml::ct2ctml(inname.c_str(), debug);
        } catch (std::exception& err) {
            writelog("get_CTML_Tree: caught an exception:\n");
            writelog(err.what());
        }
        string ffull = inname.substr(0,idot) + ".xml";
        ff = "./" + getBaseName(ffull) + ".xml";
#ifdef DEBUG_PATHS
        writelogf("ffull name = %s\n", ffull.c_str());
        writelogf("ff name = %s\n", ff.c_str());
#endif
    } else {
        ff = inname;
    }
#ifdef DEBUG_PATHS
    writelog("Attempting to parse xml file " + ff + "\n");
#else
    if (debug > 0) {
        writelog("Attempting to parse xml file " + ff + "\n");
    }
#endif
    ifstream fin(ff.c_str());
    if (!fin) {
        throw
        CanteraError("get_CTML_Tree",
                     "XML file " + ff + " not found");
    }
    rootPtr->build(fin);
    fin.close();
}
}
<|endoftext|>


<commit_before>#include "neuralnet/project.h"

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(project_tests)

BOOST_AUTO_TEST_CASE(project_AddProjectsToWhitelistShouldBePossible)
{
    NN::Whitelist whitelist;
    whitelist.Add("Enigma", "http://enigma.test", 1234567);

    auto snapshot = whitelist.Snapshot();
    BOOST_CHECK(snapshot.Contains("Enigma") == true);
}

BOOST_AUTO_TEST_CASE(project_EmptySnapshotsShouldNotBePopulated)
{
    NN::Whitelist whitelist;
    BOOST_CHECK(whitelist.Snapshot().Populated() == false);
}

BOOST_AUTO_TEST_CASE(project_PopulatedSnapshotsShouldBePopulated)
{
    NN::Whitelist whitelist;
    whitelist.Add("Enigma", "http://enigma.test", 1234567);
    BOOST_CHECK(whitelist.Snapshot().Populated() == true);
}

BOOST_AUTO_TEST_CASE(project_DeletingProjectsShouldBePossible)
{
    NN::Whitelist whitelist;
    whitelist.Add("Enigma", "http://enigma.test", 1234567);
    whitelist.Delete("Enigma");

    auto snapshot = whitelist.Snapshot();
    BOOST_CHECK(snapshot.Contains("Enigma") == false);
}

BOOST_AUTO_TEST_CASE(project_SnapshotsShouldContainDeletedProjects)
{
    NN::Whitelist whitelist;
    whitelist.Add("Enigma", "http://enigma.test", 1234567);

    auto snapshot = whitelist.Snapshot();
    whitelist.Delete("Enigma");
    BOOST_CHECK(snapshot.Contains("Enigma") == true);
}

BOOST_AUTO_TEST_CASE(project_ReAddingAProjectShouldOverwriteIt)
{
    NN::Whitelist whitelist;
    whitelist.Add("Enigma", "http://enigma.test", 1234567);
    whitelist.Add("Enigma", "http://new.enigma.test", 1234567);

    auto snapshot = whitelist.Snapshot();
    BOOST_CHECK(snapshot.size() == 1);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Finish writing tests for neural network project whitelist API<commit_after>#include "neuralnet/project.h"

#include <boost/test/unit_test.hpp>

// -----------------------------------------------------------------------------
// Project
// -----------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(Project)

BOOST_AUTO_TEST_CASE(it_provides_access_to_project_contract_data)
{
    NN::Project project("Enigma", "http://enigma.test/@", 1234567);

    BOOST_CHECK(project.m_name == "Enigma");
    BOOST_CHECK(project.m_url == "http://enigma.test/@");
    BOOST_CHECK(project.m_timestamp == 1234567);
}

BOOST_AUTO_TEST_CASE(it_formats_the_user_friendly_display_name)
{
    NN::Project project("Enigma_at_Home", "http://enigma.test/@", 1234567);

    BOOST_CHECK(project.DisplayName() == "Enigma at Home");
}

BOOST_AUTO_TEST_CASE(it_formats_the_base_project_url)
{
    NN::Project project("Enigma", "http://enigma.test/@", 1234567);

    BOOST_CHECK(project.BaseUrl() == "http://enigma.test/");
}

BOOST_AUTO_TEST_CASE(it_formats_the_project_display_url)
{
    NN::Project project("Enigma", "http://enigma.test/@", 1234567);

    BOOST_CHECK(project.DisplayUrl() == "http://enigma.test/");
}

BOOST_AUTO_TEST_CASE(it_formats_the_project_stats_url)
{
    NN::Project project("Enigma", "http://enigma.test/@", 1234567);

    BOOST_CHECK(project.StatsUrl() == "http://enigma.test/stats/");
}

BOOST_AUTO_TEST_CASE(it_formats_a_project_stats_archive_url)
{
    NN::Project project("Enigma", "http://enigma.test/@", 1234567);

    BOOST_CHECK(project.StatsUrl("user") == "http://enigma.test/stats/user.gz");
    BOOST_CHECK(project.StatsUrl("team") == "http://enigma.test/stats/team.gz");
}

BOOST_AUTO_TEST_SUITE_END()

// -----------------------------------------------------------------------------
// WhitelistSnapshot
// -----------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(WhitelistSnapshot)

BOOST_AUTO_TEST_CASE(it_is_iterable)
{
    NN::WhitelistSnapshot s(std::make_shared<NN::ProjectList>(NN::ProjectList {
        NN::Project("Enigma", "http://enigma.test/@", 1234567),
        NN::Project("Einstein@home", "http://einsteinathome.org/@", 1234567)
    }));

    auto counter = 0;

    for (auto const& project : s) {
        BOOST_CHECK(project.m_timestamp == 1234567);
        counter++;
    }

    BOOST_CHECK(counter == 2);
}

BOOST_AUTO_TEST_CASE(it_counts_the_number_of_projects)
{
    NN::WhitelistSnapshot s1(std::make_shared<NN::ProjectList>());

    BOOST_CHECK(s1.size() == 0);

    NN::WhitelistSnapshot s2(std::make_shared<NN::ProjectList>(NN::ProjectList {
        NN::Project("Enigma", "http://enigma.test/@", 1234567),
        NN::Project("Einstein@home", "http://einsteinathome.org/@", 1234567)
    }));

    BOOST_CHECK(s2.size() == 2);
}

BOOST_AUTO_TEST_CASE(it_indicates_whether_it_contains_any_projects)
{
    NN::WhitelistSnapshot s1(std::make_shared<NN::ProjectList>());

    BOOST_CHECK(s1.Populated() == false);

    NN::WhitelistSnapshot s2(std::make_shared<NN::ProjectList>(NN::ProjectList {
        NN::Project("Enigma", "http://enigma.test/@", 1234567),
        NN::Project("Einstein@home", "http://einsteinathome.org/@", 1234567)
    }));

    BOOST_CHECK(s2.Populated() == true);
}

BOOST_AUTO_TEST_CASE(it_sorts_a_copy_of_the_projects_by_name)
{
    // WhitelistSnapshot performs a case-insensitive sort, so we add an upper-
    // case project name to verify:
    NN::WhitelistSnapshot snapshot = NN::WhitelistSnapshot(
        std::make_shared<NN::ProjectList>(NN::ProjectList {
            NN::Project("c", "http://c.example.com/@", 1234567),
            NN::Project("a", "http://a.example.com/@", 1234567),
            NN::Project("B", "http://b.example.com/@", 1234567),
        }))
        .Sorted();

    auto counter = 0;

    // Project doesn't implement comparison or stream operators, so we cannot
    // use the BOOST_CHECK_EQUAL_COLLECTIONS assertion:
    for (auto const& project : snapshot) {
        switch (counter) {
            case 0: BOOST_CHECK(project.m_name == "a"); break;
            case 1: BOOST_CHECK(project.m_name == "B"); break;
            case 2: BOOST_CHECK(project.m_name == "c"); break;
        }

        counter++;
    }
}

BOOST_AUTO_TEST_SUITE_END()

// -----------------------------------------------------------------------------
// Whitelist
// -----------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(Whitelist)

BOOST_AUTO_TEST_CASE(it_adds_whitelisted_projects_from_contract_data)
{
    NN::Whitelist whitelist;

    BOOST_CHECK(whitelist.Snapshot().size() == 0);
    BOOST_CHECK(whitelist.Snapshot().Contains("Enigma") == false);

    whitelist.Add("Enigma", "http://enigma.test", 1234567);

    BOOST_CHECK(whitelist.Snapshot().size() == 1);
    BOOST_CHECK(whitelist.Snapshot().Contains("Enigma") == true);
}

BOOST_AUTO_TEST_CASE(it_removes_whitelisted_projects_from_contract_data)
{
    NN::Whitelist whitelist;
    whitelist.Add("Enigma", "http://enigma.test", 1234567);

    BOOST_CHECK(whitelist.Snapshot().size() == 1);
    BOOST_CHECK(whitelist.Snapshot().Contains("Enigma") == true);

    whitelist.Delete("Enigma");

    BOOST_CHECK(whitelist.Snapshot().size() == 0);
    BOOST_CHECK(whitelist.Snapshot().Contains("Enigma") == false);
}

BOOST_AUTO_TEST_CASE(it_does_not_mutate_existing_snapshots)
{
    NN::Whitelist whitelist;
    whitelist.Add("Enigma", "http://enigma.test", 1234567);

    auto snapshot = whitelist.Snapshot();
    whitelist.Delete("Enigma");

    BOOST_CHECK(snapshot.Contains("Enigma") == true);
}

BOOST_AUTO_TEST_CASE(it_overwrites_projects_with_the_same_name)
{
    NN::Whitelist whitelist;
    whitelist.Add("Enigma", "http://enigma.test", 1234567);
    whitelist.Add("Enigma", "http://new.enigma.test", 1234567);

    auto snapshot = whitelist.Snapshot();
    BOOST_CHECK(snapshot.size() == 1);

    for (const auto& project : snapshot) {
        BOOST_CHECK(project.m_url == "http://new.enigma.test");
    }
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdint.h>

#include "src/trace_processor/slice_tracker.h"
#include "src/trace_processor/trace_processor_context.h"
#include "src/trace_processor/trace_storage.h"

namespace perfetto {
namespace trace_processor {

SliceTracker::SliceTracker(TraceProcessorContext* context)
    : context_(context) {}

SliceTracker::~SliceTracker() = default;

void SliceTracker::Begin(uint64_t timestamp,
                         UniqueTid utid,
                         StringId cat,
                         StringId name) {
  auto& stack = threads_[utid];
  MaybeCloseStack(timestamp, stack);
  stack.emplace_back(Slice{cat, name, timestamp, 0});
}

void SliceTracker::Scoped(uint64_t timestamp,
                          UniqueTid utid,
                          StringId cat,
                          StringId name,
                          uint64_t duration) {
  auto& stack = threads_[utid];
  MaybeCloseStack(timestamp, stack);
  stack.emplace_back(Slice{cat, name, timestamp, timestamp + duration});
  CompleteSlice(utid);
}

void SliceTracker::End(uint64_t timestamp,
                       UniqueTid utid,
                       StringId cat,
                       StringId name) {
  auto& stack = threads_[utid];
  MaybeCloseStack(timestamp, stack);
  PERFETTO_CHECK(!stack.empty());

  PERFETTO_CHECK(cat == 0 || stack.back().cat_id == cat);
  PERFETTO_CHECK(name == 0 || stack.back().name_id == name);

  Slice& slice = stack.back();
  slice.end_ts = timestamp;

  CompleteSlice(utid);
  // TODO(primiano): auto-close B slices left open at the end.
}

void SliceTracker::CompleteSlice(UniqueTid utid) {
  auto& stack = threads_[utid];
  if (stack.size() >= std::numeric_limits<uint8_t>::max()) {
    stack.pop_back();
    return;
  }
  const uint8_t depth = static_cast<uint8_t>(stack.size()) - 1;

  uint64_t parent_stack_id, stack_id;
  std::tie(parent_stack_id, stack_id) = GetStackHashes(stack);

  Slice& slice = stack.back();
  auto* slices = context_->storage->mutable_nestable_slices();
  slices->AddSlice(slice.start_ts, slice.end_ts - slice.start_ts, utid, 0,
                   slice.name_id, depth, stack_id, parent_stack_id);

  stack.pop_back();
}

void SliceTracker::MaybeCloseStack(uint64_t ts, SlicesStack& stack) {
  bool check_only = false;
  for (int i = static_cast<int>(stack.size()) - 1; i >= 0; i--) {
    const Slice& slice = stack[size_t(i)];
    if (slice.end_ts == 0) {
      check_only = true;
    }

    if (check_only) {
      PERFETTO_DCHECK(ts >= slice.start_ts);
      PERFETTO_DCHECK(slice.end_ts == 0 || ts <= slice.end_ts);
      continue;
    }

    if (slice.end_ts <= ts) {
      stack.pop_back();
    }
  }
}

// Returns <parent_stack_id, stack_id>, where
// |parent_stack_id| == hash(stack_id - last slice).
std::tuple<uint64_t, uint64_t> SliceTracker::GetStackHashes(
    const SlicesStack& stack) {
  PERFETTO_DCHECK(!stack.empty());
  std::string s;
  s.reserve(stack.size() * sizeof(uint64_t) * 2);
  constexpr uint64_t kMask = uint64_t(-1) >> 1;
  uint64_t parent_stack_id = 0;
  for (size_t i = 0; i < stack.size(); i++) {
    if (i == stack.size() - 1)
      parent_stack_id = i > 0 ? (std::hash<std::string>{}(s)) & kMask : 0;
    const Slice& slice = stack[i];
    s.append(reinterpret_cast<const char*>(&slice.cat_id),
             sizeof(slice.cat_id));
    s.append(reinterpret_cast<const char*>(&slice.name_id),
             sizeof(slice.name_id));
  }
  uint64_t stack_id = (std::hash<std::string>{}(s)) & kMask;
  return std::make_tuple(parent_stack_id, stack_id);
}

}  // namespace trace_processor
}  // namespace perfetto
<commit_msg>Fix GCC build am: 77304e28a6<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <limits>

#include <stdint.h>

#include "src/trace_processor/slice_tracker.h"
#include "src/trace_processor/trace_processor_context.h"
#include "src/trace_processor/trace_storage.h"

namespace perfetto {
namespace trace_processor {

SliceTracker::SliceTracker(TraceProcessorContext* context)
    : context_(context) {}

SliceTracker::~SliceTracker() = default;

void SliceTracker::Begin(uint64_t timestamp,
                         UniqueTid utid,
                         StringId cat,
                         StringId name) {
  auto& stack = threads_[utid];
  MaybeCloseStack(timestamp, stack);
  stack.emplace_back(Slice{cat, name, timestamp, 0});
}

void SliceTracker::Scoped(uint64_t timestamp,
                          UniqueTid utid,
                          StringId cat,
                          StringId name,
                          uint64_t duration) {
  auto& stack = threads_[utid];
  MaybeCloseStack(timestamp, stack);
  stack.emplace_back(Slice{cat, name, timestamp, timestamp + duration});
  CompleteSlice(utid);
}

void SliceTracker::End(uint64_t timestamp,
                       UniqueTid utid,
                       StringId cat,
                       StringId name) {
  auto& stack = threads_[utid];
  MaybeCloseStack(timestamp, stack);
  PERFETTO_CHECK(!stack.empty());

  PERFETTO_CHECK(cat == 0 || stack.back().cat_id == cat);
  PERFETTO_CHECK(name == 0 || stack.back().name_id == name);

  Slice& slice = stack.back();
  slice.end_ts = timestamp;

  CompleteSlice(utid);
  // TODO(primiano): auto-close B slices left open at the end.
}

void SliceTracker::CompleteSlice(UniqueTid utid) {
  auto& stack = threads_[utid];
  if (stack.size() >= std::numeric_limits<uint8_t>::max()) {
    stack.pop_back();
    return;
  }
  const uint8_t depth = static_cast<uint8_t>(stack.size()) - 1;

  uint64_t parent_stack_id, stack_id;
  std::tie(parent_stack_id, stack_id) = GetStackHashes(stack);

  Slice& slice = stack.back();
  auto* slices = context_->storage->mutable_nestable_slices();
  slices->AddSlice(slice.start_ts, slice.end_ts - slice.start_ts, utid, 0,
                   slice.name_id, depth, stack_id, parent_stack_id);

  stack.pop_back();
}

void SliceTracker::MaybeCloseStack(uint64_t ts, SlicesStack& stack) {
  bool check_only = false;
  for (int i = static_cast<int>(stack.size()) - 1; i >= 0; i--) {
    const Slice& slice = stack[size_t(i)];
    if (slice.end_ts == 0) {
      check_only = true;
    }

    if (check_only) {
      PERFETTO_DCHECK(ts >= slice.start_ts);
      PERFETTO_DCHECK(slice.end_ts == 0 || ts <= slice.end_ts);
      continue;
    }

    if (slice.end_ts <= ts) {
      stack.pop_back();
    }
  }
}

// Returns <parent_stack_id, stack_id>, where
// |parent_stack_id| == hash(stack_id - last slice).
std::tuple<uint64_t, uint64_t> SliceTracker::GetStackHashes(
    const SlicesStack& stack) {
  PERFETTO_DCHECK(!stack.empty());
  std::string s;
  s.reserve(stack.size() * sizeof(uint64_t) * 2);
  constexpr uint64_t kMask = uint64_t(-1) >> 1;
  uint64_t parent_stack_id = 0;
  for (size_t i = 0; i < stack.size(); i++) {
    if (i == stack.size() - 1)
      parent_stack_id = i > 0 ? (std::hash<std::string>{}(s)) & kMask : 0;
    const Slice& slice = stack[i];
    s.append(reinterpret_cast<const char*>(&slice.cat_id),
             sizeof(slice.cat_id));
    s.append(reinterpret_cast<const char*>(&slice.name_id),
             sizeof(slice.name_id));
  }
  uint64_t stack_id = (std::hash<std::string>{}(s)) & kMask;
  return std::make_tuple(parent_stack_id, stack_id);
}

}  // namespace trace_processor
}  // namespace perfetto
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/trace_processor/trace_storage.h"

#include <string.h>

namespace perfetto {
namespace trace_processor {

TraceStorage::TraceStorage() {
  // Upid 0 is reserved for invalid processes.
  unique_processes_.emplace_back();
}

TraceStorage::~TraceStorage() {}

void TraceStorage::PushSchedSwitch(uint32_t cpu,
                                   uint64_t timestamp,
                                   uint32_t prev_pid,
                                   uint32_t prev_state,
                                   const char* prev_comm,
                                   size_t prev_comm_len,
                                   uint32_t next_pid) {
  SchedSwitchEvent* prev = &last_sched_per_cpu_[cpu];

  // If we had a valid previous event, then inform the storage about the
  // slice.
  if (prev->valid()) {
    uint64_t duration = timestamp - prev->timestamp;
    cpu_events_[cpu].AddSlice(prev->timestamp, duration, prev->prev_thread_id);
  }

  // If the this events previous pid does not match the previous event's next
  // pid, make a note of this.
  if (prev_pid != prev->next_pid) {
    stats_.mismatched_sched_switch_tids_++;
  }

  // Update the map with the current event.
  prev->cpu = cpu;
  prev->timestamp = timestamp;
  prev->prev_pid = prev_pid;
  prev->prev_state = prev_state;
  prev->prev_thread_id = InternString(prev_comm, prev_comm_len);
  prev->next_pid = next_pid;
}

void TraceStorage::PushProcess(uint32_t pid,
                               const char* process_name,
                               size_t process_name_len) {
  bool exists = false;
  auto pids_pair = pids_.equal_range(pid);
  auto proc_name_id = InternString(process_name, process_name_len);
  if (pids_pair.first != pids_pair.second) {
    UniquePid prev_upid = std::prev(pids_pair.second)->second;
    // If the previous process with the same pid also has the same name,
    // then no action needs to be taken.
    exists = unique_processes_[prev_upid].process_name_id == proc_name_id;
  }

  if (!exists) {
    pids_.emplace(pid, current_upid_++);
    ProcessEntry new_process;
    new_process.start_ns = 0;
    new_process.end_ns = 0;
    new_process.process_name_id = proc_name_id;
    unique_processes_.emplace_back(std::move(new_process));
  }
}

TraceStorage::UniqueProcessRange TraceStorage::UpidsForPid(uint32_t pid) {
  return pids_.equal_range(pid);
}

TraceStorage::StringId TraceStorage::InternString(const char* data,
                                                  size_t length) {
  uint32_t hash = 0;
  for (size_t i = 0; i < length; ++i) {
    hash = static_cast<uint32_t>(data[i]) + (hash * 31);
  }
  auto id_it = string_index_.find(hash);
  if (id_it != string_index_.end()) {
    // TODO(lalitm): check if this DCHECK happens and if so, then change hash
    // to 64bit.
    PERFETTO_DCHECK(
        strncmp(string_pool_[id_it->second].c_str(), data, length) == 0);
    return id_it->second;
  }
  string_pool_.emplace_back(data, length);
  StringId string_id = string_pool_.size() - 1;
  string_index_.emplace(hash, string_id);
  return string_id;
}

}  // namespace trace_processor
}  // namespace perfetto
<commit_msg>trace_processor: don't add swapper slices to trace storage am: 0ff6498e41<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/trace_processor/trace_storage.h"

#include <string.h>

namespace perfetto {
namespace trace_processor {

TraceStorage::TraceStorage() {
  // Upid 0 is reserved for invalid processes.
  unique_processes_.emplace_back();
}

TraceStorage::~TraceStorage() {}

void TraceStorage::PushSchedSwitch(uint32_t cpu,
                                   uint64_t timestamp,
                                   uint32_t prev_pid,
                                   uint32_t prev_state,
                                   const char* prev_comm,
                                   size_t prev_comm_len,
                                   uint32_t next_pid) {
  SchedSwitchEvent* prev = &last_sched_per_cpu_[cpu];

  // If we had a valid previous event, then inform the storage about the
  // slice.
  if (prev->valid() && prev->next_pid != 0 /* Idle process (swapper/N) */) {
    uint64_t duration = timestamp - prev->timestamp;
    cpu_events_[cpu].AddSlice(prev->timestamp, duration, prev->prev_thread_id);
  }

  // If the this events previous pid does not match the previous event's next
  // pid, make a note of this.
  if (prev_pid != prev->next_pid) {
    stats_.mismatched_sched_switch_tids_++;
  }

  // Update the map with the current event.
  prev->cpu = cpu;
  prev->timestamp = timestamp;
  prev->prev_pid = prev_pid;
  prev->prev_state = prev_state;
  prev->prev_thread_id = InternString(prev_comm, prev_comm_len);
  prev->next_pid = next_pid;
}

void TraceStorage::PushProcess(uint32_t pid,
                               const char* process_name,
                               size_t process_name_len) {
  bool exists = false;
  auto pids_pair = pids_.equal_range(pid);
  auto proc_name_id = InternString(process_name, process_name_len);
  if (pids_pair.first != pids_pair.second) {
    UniquePid prev_upid = std::prev(pids_pair.second)->second;
    // If the previous process with the same pid also has the same name,
    // then no action needs to be taken.
    exists = unique_processes_[prev_upid].process_name_id == proc_name_id;
  }

  if (!exists) {
    pids_.emplace(pid, current_upid_++);
    ProcessEntry new_process;
    new_process.start_ns = 0;
    new_process.end_ns = 0;
    new_process.process_name_id = proc_name_id;
    unique_processes_.emplace_back(std::move(new_process));
  }
}

TraceStorage::UniqueProcessRange TraceStorage::UpidsForPid(uint32_t pid) {
  return pids_.equal_range(pid);
}

TraceStorage::StringId TraceStorage::InternString(const char* data,
                                                  size_t length) {
  uint32_t hash = 0;
  for (size_t i = 0; i < length; ++i) {
    hash = static_cast<uint32_t>(data[i]) + (hash * 31);
  }
  auto id_it = string_index_.find(hash);
  if (id_it != string_index_.end()) {
    // TODO(lalitm): check if this DCHECK happens and if so, then change hash
    // to 64bit.
    PERFETTO_DCHECK(
        strncmp(string_pool_[id_it->second].c_str(), data, length) == 0);
    return id_it->second;
  }
  string_pool_.emplace_back(data, length);
  StringId string_id = string_pool_.size() - 1;
  string_index_.emplace(hash, string_id);
  return string_id;
}

}  // namespace trace_processor
}  // namespace perfetto
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/traced/probes/probes_producer.h"

#include <stdio.h>
#include <sys/stat.h>
#include <queue>
#include <string>

#include "perfetto/base/logging.h"
#include "perfetto/base/weak_ptr.h"
#include "perfetto/traced/traced.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/ftrace_config.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "src/traced/probes/filesystem/inode_file_data_source.h"

#include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
#include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
#include "perfetto/trace/trace_packet.pbzero.h"

namespace perfetto {
namespace {

uint64_t kInitialConnectionBackoffMs = 100;
uint64_t kMaxConnectionBackoffMs = 30 * 1000;
constexpr char kFtraceSourceName[] = "linux.ftrace";
constexpr char kProcessStatsSourceName[] = "linux.process_stats";
constexpr char kInodeMapSourceName[] = "linux.inode_file_map";

}  // namespace.

// State transition diagram:
//                    +----------------------------+
//                    v                            +
// NotStarted -> NotConnected -> Connecting -> Connected
//                    ^              +
//                    +--------------+
//

ProbesProducer::ProbesProducer() {}
ProbesProducer::~ProbesProducer() = default;

void ProbesProducer::OnConnect() {
  PERFETTO_DCHECK(state_ == kConnecting);
  state_ = kConnected;
  ResetConnectionBackoff();
  PERFETTO_LOG("Connected to the service");

  DataSourceDescriptor ftrace_descriptor;
  ftrace_descriptor.set_name(kFtraceSourceName);
  endpoint_->RegisterDataSource(ftrace_descriptor);

  DataSourceDescriptor process_stats_descriptor;
  process_stats_descriptor.set_name(kProcessStatsSourceName);
  endpoint_->RegisterDataSource(process_stats_descriptor);

  DataSourceDescriptor inode_map_descriptor;
  inode_map_descriptor.set_name(kInodeMapSourceName);
  endpoint_->RegisterDataSource(inode_map_descriptor);
}

void ProbesProducer::OnDisconnect() {
  PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
  state_ = kNotConnected;
  PERFETTO_LOG("Disconnected from tracing service");
  IncreaseConnectionBackoff();

  // TODO(hjd): Erase all sinks and add e2e test for this.
  task_runner_->PostDelayedTask([this] { this->Connect(); },
                                connection_backoff_ms_);
}

void ProbesProducer::CreateDataSourceInstance(DataSourceInstanceID instance_id,
                                              const DataSourceConfig& config) {
  // TODO(hjd): This a hack since we don't actually know the session id. For
  // now we'll assume anything wit hthe same target buffer is in the same
  // session.
  TracingSessionID session_id = config.target_buffer();

  if (config.name() == kFtraceSourceName) {
    if (!CreateFtraceDataSourceInstance(session_id, instance_id, config))
      failed_sources_.insert(instance_id);
  } else if (config.name() == kInodeMapSourceName) {
    CreateInodeFileDataSourceInstance(session_id, instance_id, config);
  } else if (config.name() == kProcessStatsSourceName) {
    CreateProcessStatsDataSourceInstance(session_id, instance_id, config);
  } else {
    PERFETTO_ELOG("Data source name: %s not recognised.",
                  config.name().c_str());
    return;
  }

  std::map<TracingSessionID, InodeFileDataSource*> file_sources;
  std::map<TracingSessionID, ProcessStatsDataSource*> ps_sources;
  for (const auto& pair : file_map_sources_)
    file_sources[pair.second->session_id()] = pair.second.get();
  for (const auto& pair : process_stats_sources_)
    ps_sources[pair.second->session_id()] = pair.second.get();

  for (const auto& id_to_source : delegates_) {
    const std::unique_ptr<SinkDelegate>& source = id_to_source.second;
    if (session_id != source->session_id())
      continue;
    if (!source->ps_source() && ps_sources.count(session_id))
      source->set_ps_source(ps_sources[session_id]->GetWeakPtr());
    if (!source->file_source() && file_sources.count(session_id))
      source->set_file_source(file_sources[session_id]->GetWeakPtr());
  }
}

void ProbesProducer::AddWatchdogsTimer(DataSourceInstanceID id,
                                       const DataSourceConfig& config) {
  if (config.trace_duration_ms() != 0)
    watchdogs_.emplace(id, base::Watchdog::GetInstance()->CreateFatalTimer(
                               5000 + 2 * config.trace_duration_ms()));
}

bool ProbesProducer::CreateFtraceDataSourceInstance(
    TracingSessionID session_id,
    DataSourceInstanceID id,
    const DataSourceConfig& config) {
  // Don't retry if FtraceController::Create() failed once.
  // This can legitimately happen on user builds where we cannot access the
  // debug paths, e.g., because of SELinux rules.
  if (ftrace_creation_failed_)
    return false;

  // Lazily create on the first instance.
  if (!ftrace_) {
    ftrace_ = FtraceController::Create(task_runner_);

    if (!ftrace_) {
      PERFETTO_ELOG("Failed to create FtraceController");
      ftrace_creation_failed_ = true;
      return false;
    }

    ftrace_->DisableAllEvents();
    ftrace_->ClearTrace();
  }

  PERFETTO_LOG("Ftrace start (id=%" PRIu64 ", target_buf=%" PRIu32 ")", id,
               config.target_buffer());

  FtraceConfig proto_config = config.ftrace_config();

  // TODO(hjd): Static cast is bad, target_buffer() should return a BufferID.
  auto trace_writer = endpoint_->CreateTraceWriter(
      static_cast<BufferID>(config.target_buffer()));
  auto delegate = std::unique_ptr<SinkDelegate>(
      new SinkDelegate(session_id, task_runner_, std::move(trace_writer)));
  auto sink = ftrace_->CreateSink(std::move(proto_config), delegate.get());
  if (!sink) {
    PERFETTO_ELOG("Failed to start tracing (maybe someone else is using it?)");
    return false;
  }
  delegate->set_sink(std::move(sink));
  delegates_.emplace(id, std::move(delegate));
  AddWatchdogsTimer(id, config);
  return true;
}

void ProbesProducer::CreateInodeFileDataSourceInstance(
    TracingSessionID session_id,
    DataSourceInstanceID id,
    DataSourceConfig source_config) {
  PERFETTO_LOG("Inode file map start (id=%" PRIu64 ", target_buf=%" PRIu32 ")",
               id, source_config.target_buffer());
  auto trace_writer = endpoint_->CreateTraceWriter(
      static_cast<BufferID>(source_config.target_buffer()));
  if (system_inodes_.empty())
    CreateStaticDeviceToInodeMap("/system", &system_inodes_);
  auto file_map_source =
      std::unique_ptr<InodeFileDataSource>(new InodeFileDataSource(
          std::move(source_config), task_runner_, session_id, &system_inodes_,
          &cache_, std::move(trace_writer)));
  file_map_sources_.emplace(id, std::move(file_map_source));
  AddWatchdogsTimer(id, source_config);
}

void ProbesProducer::CreateProcessStatsDataSourceInstance(
    TracingSessionID session_id,
    DataSourceInstanceID id,
    const DataSourceConfig& config) {
  PERFETTO_DCHECK(process_stats_sources_.count(id) == 0);
  auto trace_writer = endpoint_->CreateTraceWriter(
      static_cast<BufferID>(config.target_buffer()));
  auto source = std::unique_ptr<ProcessStatsDataSource>(
      new ProcessStatsDataSource(session_id, std::move(trace_writer)));
  auto it_and_inserted = process_stats_sources_.emplace(id, std::move(source));
  PERFETTO_DCHECK(it_and_inserted.second);
  if (std::find(config.process_stats_config().quirks().begin(),
                config.process_stats_config().quirks().end(),
                ProcessStatsConfig::DISABLE_INITIAL_DUMP) !=
      config.process_stats_config().quirks().end()) {
    PERFETTO_DLOG("Initial process tree dump is disabled.");
    return;
  }
  it_and_inserted.first->second->WriteAllProcesses();
}

void ProbesProducer::TearDownDataSourceInstance(DataSourceInstanceID id) {
  PERFETTO_LOG("Producer stop (id=%" PRIu64 ")", id);
  // |id| could be the id of any of the datasources we handle:
  PERFETTO_DCHECK((failed_sources_.count(id) + delegates_.count(id) +
                   process_stats_sources_.count(id) +
                   file_map_sources_.count(id)) == 1);
  failed_sources_.erase(id);
  delegates_.erase(id);
  process_stats_sources_.erase(id);
  file_map_sources_.erase(id);
  watchdogs_.erase(id);
}

void ProbesProducer::OnTracingStart() {}
void ProbesProducer::OnTracingStop() {}

void ProbesProducer::ConnectWithRetries(const char* socket_name,
                                        base::TaskRunner* task_runner) {
  PERFETTO_DCHECK(state_ == kNotStarted);
  state_ = kNotConnected;

  ResetConnectionBackoff();
  socket_name_ = socket_name;
  task_runner_ = task_runner;
  Connect();
}

void ProbesProducer::Connect() {
  PERFETTO_DCHECK(state_ == kNotConnected);
  state_ = kConnecting;
  endpoint_ = ProducerIPCClient::Connect(
      socket_name_, this, "perfetto.traced_probes", task_runner_);
}

void ProbesProducer::IncreaseConnectionBackoff() {
  connection_backoff_ms_ *= 2;
  if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
    connection_backoff_ms_ = kMaxConnectionBackoffMs;
}

void ProbesProducer::ResetConnectionBackoff() {
  connection_backoff_ms_ = kInitialConnectionBackoffMs;
}

ProbesProducer::SinkDelegate::SinkDelegate(TracingSessionID id,
                                           base::TaskRunner* task_runner,
                                           std::unique_ptr<TraceWriter> writer)
    : session_id_(id),
      task_runner_(task_runner),
      writer_(std::move(writer)),
      weak_factory_(this) {}

ProbesProducer::SinkDelegate::~SinkDelegate() = default;

ProbesProducer::FtraceBundleHandle
ProbesProducer::SinkDelegate::GetBundleForCpu(size_t) {
  trace_packet_ = writer_->NewTracePacket();
  return FtraceBundleHandle(trace_packet_->set_ftrace_events());
}

void ProbesProducer::SinkDelegate::OnBundleComplete(
    size_t,
    FtraceBundleHandle,
    const FtraceMetadata& metadata) {
  trace_packet_->Finalize();

  if (ps_source_ && !metadata.pids.empty()) {
    const auto& pids = metadata.pids;
    auto weak_ps_source = ps_source_;
    task_runner_->PostTask([weak_ps_source, pids] {
      if (weak_ps_source)
        weak_ps_source->OnPids(pids);
    });
  }

  if (file_source_ && !metadata.inode_and_device.empty()) {
    auto inodes = metadata.inode_and_device;
    auto weak_file_source = file_source_;
    task_runner_->PostTask([weak_file_source, inodes] {
      if (weak_file_source)
        weak_file_source->OnInodes(inodes);
    });
  }
}

}  // namespace perfetto
<commit_msg>Fix GCC build (again) am: 5252660bfe am: eb51c078c7<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/traced/probes/probes_producer.h"

#include <stdio.h>
#include <sys/stat.h>

#include <algorithm>
#include <queue>
#include <string>

#include "perfetto/base/logging.h"
#include "perfetto/base/weak_ptr.h"
#include "perfetto/traced/traced.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/ftrace_config.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "src/traced/probes/filesystem/inode_file_data_source.h"

#include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
#include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
#include "perfetto/trace/trace_packet.pbzero.h"

namespace perfetto {
namespace {

uint64_t kInitialConnectionBackoffMs = 100;
uint64_t kMaxConnectionBackoffMs = 30 * 1000;
constexpr char kFtraceSourceName[] = "linux.ftrace";
constexpr char kProcessStatsSourceName[] = "linux.process_stats";
constexpr char kInodeMapSourceName[] = "linux.inode_file_map";

}  // namespace.

// State transition diagram:
//                    +----------------------------+
//                    v                            +
// NotStarted -> NotConnected -> Connecting -> Connected
//                    ^              +
//                    +--------------+
//

ProbesProducer::ProbesProducer() {}
ProbesProducer::~ProbesProducer() = default;

void ProbesProducer::OnConnect() {
  PERFETTO_DCHECK(state_ == kConnecting);
  state_ = kConnected;
  ResetConnectionBackoff();
  PERFETTO_LOG("Connected to the service");

  DataSourceDescriptor ftrace_descriptor;
  ftrace_descriptor.set_name(kFtraceSourceName);
  endpoint_->RegisterDataSource(ftrace_descriptor);

  DataSourceDescriptor process_stats_descriptor;
  process_stats_descriptor.set_name(kProcessStatsSourceName);
  endpoint_->RegisterDataSource(process_stats_descriptor);

  DataSourceDescriptor inode_map_descriptor;
  inode_map_descriptor.set_name(kInodeMapSourceName);
  endpoint_->RegisterDataSource(inode_map_descriptor);
}

void ProbesProducer::OnDisconnect() {
  PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
  state_ = kNotConnected;
  PERFETTO_LOG("Disconnected from tracing service");
  IncreaseConnectionBackoff();

  // TODO(hjd): Erase all sinks and add e2e test for this.
  task_runner_->PostDelayedTask([this] { this->Connect(); },
                                connection_backoff_ms_);
}

void ProbesProducer::CreateDataSourceInstance(DataSourceInstanceID instance_id,
                                              const DataSourceConfig& config) {
  // TODO(hjd): This a hack since we don't actually know the session id. For
  // now we'll assume anything wit hthe same target buffer is in the same
  // session.
  TracingSessionID session_id = config.target_buffer();

  if (config.name() == kFtraceSourceName) {
    if (!CreateFtraceDataSourceInstance(session_id, instance_id, config))
      failed_sources_.insert(instance_id);
  } else if (config.name() == kInodeMapSourceName) {
    CreateInodeFileDataSourceInstance(session_id, instance_id, config);
  } else if (config.name() == kProcessStatsSourceName) {
    CreateProcessStatsDataSourceInstance(session_id, instance_id, config);
  } else {
    PERFETTO_ELOG("Data source name: %s not recognised.",
                  config.name().c_str());
    return;
  }

  std::map<TracingSessionID, InodeFileDataSource*> file_sources;
  std::map<TracingSessionID, ProcessStatsDataSource*> ps_sources;
  for (const auto& pair : file_map_sources_)
    file_sources[pair.second->session_id()] = pair.second.get();
  for (const auto& pair : process_stats_sources_)
    ps_sources[pair.second->session_id()] = pair.second.get();

  for (const auto& id_to_source : delegates_) {
    const std::unique_ptr<SinkDelegate>& source = id_to_source.second;
    if (session_id != source->session_id())
      continue;
    if (!source->ps_source() && ps_sources.count(session_id))
      source->set_ps_source(ps_sources[session_id]->GetWeakPtr());
    if (!source->file_source() && file_sources.count(session_id))
      source->set_file_source(file_sources[session_id]->GetWeakPtr());
  }
}

void ProbesProducer::AddWatchdogsTimer(DataSourceInstanceID id,
                                       const DataSourceConfig& config) {
  if (config.trace_duration_ms() != 0)
    watchdogs_.emplace(id, base::Watchdog::GetInstance()->CreateFatalTimer(
                               5000 + 2 * config.trace_duration_ms()));
}

bool ProbesProducer::CreateFtraceDataSourceInstance(
    TracingSessionID session_id,
    DataSourceInstanceID id,
    const DataSourceConfig& config) {
  // Don't retry if FtraceController::Create() failed once.
  // This can legitimately happen on user builds where we cannot access the
  // debug paths, e.g., because of SELinux rules.
  if (ftrace_creation_failed_)
    return false;

  // Lazily create on the first instance.
  if (!ftrace_) {
    ftrace_ = FtraceController::Create(task_runner_);

    if (!ftrace_) {
      PERFETTO_ELOG("Failed to create FtraceController");
      ftrace_creation_failed_ = true;
      return false;
    }

    ftrace_->DisableAllEvents();
    ftrace_->ClearTrace();
  }

  PERFETTO_LOG("Ftrace start (id=%" PRIu64 ", target_buf=%" PRIu32 ")", id,
               config.target_buffer());

  FtraceConfig proto_config = config.ftrace_config();

  // TODO(hjd): Static cast is bad, target_buffer() should return a BufferID.
  auto trace_writer = endpoint_->CreateTraceWriter(
      static_cast<BufferID>(config.target_buffer()));
  auto delegate = std::unique_ptr<SinkDelegate>(
      new SinkDelegate(session_id, task_runner_, std::move(trace_writer)));
  auto sink = ftrace_->CreateSink(std::move(proto_config), delegate.get());
  if (!sink) {
    PERFETTO_ELOG("Failed to start tracing (maybe someone else is using it?)");
    return false;
  }
  delegate->set_sink(std::move(sink));
  delegates_.emplace(id, std::move(delegate));
  AddWatchdogsTimer(id, config);
  return true;
}

void ProbesProducer::CreateInodeFileDataSourceInstance(
    TracingSessionID session_id,
    DataSourceInstanceID id,
    DataSourceConfig source_config) {
  PERFETTO_LOG("Inode file map start (id=%" PRIu64 ", target_buf=%" PRIu32 ")",
               id, source_config.target_buffer());
  auto trace_writer = endpoint_->CreateTraceWriter(
      static_cast<BufferID>(source_config.target_buffer()));
  if (system_inodes_.empty())
    CreateStaticDeviceToInodeMap("/system", &system_inodes_);
  auto file_map_source =
      std::unique_ptr<InodeFileDataSource>(new InodeFileDataSource(
          std::move(source_config), task_runner_, session_id, &system_inodes_,
          &cache_, std::move(trace_writer)));
  file_map_sources_.emplace(id, std::move(file_map_source));
  AddWatchdogsTimer(id, source_config);
}

void ProbesProducer::CreateProcessStatsDataSourceInstance(
    TracingSessionID session_id,
    DataSourceInstanceID id,
    const DataSourceConfig& config) {
  PERFETTO_DCHECK(process_stats_sources_.count(id) == 0);
  auto trace_writer = endpoint_->CreateTraceWriter(
      static_cast<BufferID>(config.target_buffer()));
  auto source = std::unique_ptr<ProcessStatsDataSource>(
      new ProcessStatsDataSource(session_id, std::move(trace_writer)));
  auto it_and_inserted = process_stats_sources_.emplace(id, std::move(source));
  PERFETTO_DCHECK(it_and_inserted.second);
  if (std::find(config.process_stats_config().quirks().begin(),
                config.process_stats_config().quirks().end(),
                ProcessStatsConfig::DISABLE_INITIAL_DUMP) !=
      config.process_stats_config().quirks().end()) {
    PERFETTO_DLOG("Initial process tree dump is disabled.");
    return;
  }
  it_and_inserted.first->second->WriteAllProcesses();
}

void ProbesProducer::TearDownDataSourceInstance(DataSourceInstanceID id) {
  PERFETTO_LOG("Producer stop (id=%" PRIu64 ")", id);
  // |id| could be the id of any of the datasources we handle:
  PERFETTO_DCHECK((failed_sources_.count(id) + delegates_.count(id) +
                   process_stats_sources_.count(id) +
                   file_map_sources_.count(id)) == 1);
  failed_sources_.erase(id);
  delegates_.erase(id);
  process_stats_sources_.erase(id);
  file_map_sources_.erase(id);
  watchdogs_.erase(id);
}

void ProbesProducer::OnTracingStart() {}
void ProbesProducer::OnTracingStop() {}

void ProbesProducer::ConnectWithRetries(const char* socket_name,
                                        base::TaskRunner* task_runner) {
  PERFETTO_DCHECK(state_ == kNotStarted);
  state_ = kNotConnected;

  ResetConnectionBackoff();
  socket_name_ = socket_name;
  task_runner_ = task_runner;
  Connect();
}

void ProbesProducer::Connect() {
  PERFETTO_DCHECK(state_ == kNotConnected);
  state_ = kConnecting;
  endpoint_ = ProducerIPCClient::Connect(
      socket_name_, this, "perfetto.traced_probes", task_runner_);
}

void ProbesProducer::IncreaseConnectionBackoff() {
  connection_backoff_ms_ *= 2;
  if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
    connection_backoff_ms_ = kMaxConnectionBackoffMs;
}

void ProbesProducer::ResetConnectionBackoff() {
  connection_backoff_ms_ = kInitialConnectionBackoffMs;
}

ProbesProducer::SinkDelegate::SinkDelegate(TracingSessionID id,
                                           base::TaskRunner* task_runner,
                                           std::unique_ptr<TraceWriter> writer)
    : session_id_(id),
      task_runner_(task_runner),
      writer_(std::move(writer)),
      weak_factory_(this) {}

ProbesProducer::SinkDelegate::~SinkDelegate() = default;

ProbesProducer::FtraceBundleHandle
ProbesProducer::SinkDelegate::GetBundleForCpu(size_t) {
  trace_packet_ = writer_->NewTracePacket();
  return FtraceBundleHandle(trace_packet_->set_ftrace_events());
}

void ProbesProducer::SinkDelegate::OnBundleComplete(
    size_t,
    FtraceBundleHandle,
    const FtraceMetadata& metadata) {
  trace_packet_->Finalize();

  if (ps_source_ && !metadata.pids.empty()) {
    const auto& pids = metadata.pids;
    auto weak_ps_source = ps_source_;
    task_runner_->PostTask([weak_ps_source, pids] {
      if (weak_ps_source)
        weak_ps_source->OnPids(pids);
    });
  }

  if (file_source_ && !metadata.inode_and_device.empty()) {
    auto inodes = metadata.inode_and_device;
    auto weak_file_source = file_source_;
    task_runner_->PostTask([weak_file_source, inodes] {
      if (weak_file_source)
        weak_file_source->OnInodes(inodes);
    });
  }
}

}  // namespace perfetto
<|endoftext|>