<commit_before>//MIT License
//
//Copyright(c) 2016 Matthias Moeller
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files(the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.

#ifndef __TYTI_STEAM_VDF_PARSER_H__
#define __TYTI_STEAM_VDF_PARSER_H__

#include <unordered_map>
#include <utility>
#include <fstream>
#include <memory>

#include <system_error>
#include <exception>

//for wstring support
#include <locale>
#include <codecvt>
#include <string>

// internal
#include <stack>

namespace tyti
{
    namespace vdf
    {
        namespace detail
        {
            ///////////////////////////////////////////////////////////////////////////
            //  Helper functions selecting the right encoding (char/wchar_T)
            ///////////////////////////////////////////////////////////////////////////

            template<typename T>
            struct literal_macro_help
            {
                static constexpr const char* result(const char* c, const wchar_t* wc) noexcept
                {
                    return c;
                }
                static constexpr const char result(char c, wchar_t wc) noexcept
                {
                    return c;
                }
            };

            template<>
            struct literal_macro_help<wchar_t>
            {
                static constexpr const wchar_t* result(const char* c, const wchar_t* wc) noexcept
                {
                    return wc;
                }
                static constexpr const wchar_t result(char c, wchar_t wc) noexcept
                {
                    return wc;
                }
            };
#define TYTI_L(type, text) vdf::detail::literal_macro_help<type>::result(text, L##text)

            inline std::string string_converter(std::string& w)
            {
                return w;
            }

            inline std::string string_converter(std::wstring& w)
            {
                std::wstring_convert<std::codecvt_utf8<wchar_t>> conv1; // maybe wrong econding
                return conv1.to_bytes(w);
            }

            ///////////////////////////////////////////////////////////////////////////
            //  Writer helper functions
            ///////////////////////////////////////////////////////////////////////////

            struct tabs
            {
                size_t t;
                tabs(size_t i) :t(i) {}
            };

            template<typename oStreamT>
            oStreamT& operator<<(oStreamT& s, tabs t)
            {
                for (; t.t > 0; --t.t)
                    s << "\t";
                return s;
            }
        } // end namespace detail

        ///////////////////////////////////////////////////////////////////////////
        //  Interface
        ///////////////////////////////////////////////////////////////////////////

        /// basic object node. Every object has a name and can contains attributes saved as key_value pairs or childrens
        template<typename CharT>
        struct basic_object
        {
            typedef CharT char_type;
            std::basic_string<char_type> name;
            std::unordered_map<std::basic_string<char_type>, std::basic_string<char_type> > attribs;
            std::unordered_map<std::basic_string<char_type>, std::shared_ptr< basic_object<char_type> > > childs;
        };

        typedef basic_object<char> object;
        typedef basic_object<wchar_t> wobject;

        /** \brief writes given object tree in vdf format to given stream.
        Uses tabs instead of whitespaces.
        */
        template<typename oStreamT, typename charT = typename oStreamT::char_type>
        void write(oStreamT& s, const basic_object<charT>& r, size_t t = 0) noexcept(noexcept(std::operator<<(oStream&, const charT*)))
        {
            using namespace detail;
            s << tabs(t) << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tabs(t) << TYTI_L(charT, "{\n");
            for (auto& i : r.attribs)
                s << tabs(t + 1) << TYTI_L(charT, '"') << i.first << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n");
            for (auto& i : r.childs)
                write(s, i, t + 1);
            s << tabs(t) << TYTI_L(charT, "}\n");
        }

        //forward decls
        //forward decl
        template<typename iStreamT, typename charT = typename iStreamT::char_type >
        basic_object<charT> read(iStreamT& inStream, std::error_code& ec);

        /** \brief Read VDF formatted sequences defined by the range [first, last).
        If the file is mailformatted, parser will try to read it until it can.
        @param first begin iterator
        @param end end iterator
        @param ec output bool. 0 if ok, otherwise, holds an system error code

        Possible error codes:
            std::errc::protocol_error: file is mailformatted
            std::errc::not_enough_memory: not enough space
            std::errc::invalid_argument: iterators throws e.g. out of range
        */

        template<typename IterT, typename charT = typename IterT::value_type>
        basic_object<charT> read(IterT first, IterT last, std::error_code& ec) noexcept
        {
            ec.clear();

            basic_object<charT> root;
            basic_object<charT>* cur = &root;
            std::stack< basic_object<charT>* > lvls;
            //read header
            // first, quoted name
            auto b = std::find(first, last, TYTI_L(charT, '\"'));
            auto bend = std::find(b + 1, last, TYTI_L(charT, '\"'));
            root.name = std::basic_string<charT>(b + 1, bend);
            // second, get {}
            b = std::find(bend, last, TYTI_L(charT, '{'));
            try
            {
                if (b == last)
                {
                    ec = std::make_error_code(std::errc::protocol_error);
                    return root;
                }
                else
                    lvls.push(&root);
                while (!lvls.empty() && b != last)
                {
                    const std::basic_string<charT> startsym = TYTI_L(charT, "\"}");

                    //find first starting attrib/child, or ending
                    b = std::find_first_of(b, last, std::cbegin(startsym), std::cend(startsym));
                    if (*b == '\"')
                    {
                        bend = std::find(b + 1, last, TYTI_L(charT, '\"'));
                        if (bend == last)
                        {
                            ec = std::make_error_code(std::errc::protocol_error);// could not find end of name
                            return root;
                        }

                        std::basic_string<charT> curName(b + 1, bend);
                        b = bend + 1;

                        const std::basic_string<charT> ecspsym = TYTI_L(charT, "\"{");
                        b = std::find_first_of(b, last, std::cbegin(ecspsym), std::cend(ecspsym));
                        if (b == last)
                        {
                            ec = std::make_error_code(std::errc::protocol_error);// could not find 2nd part of pair
                            return root;
                        }

                        if (*b == '\"')
                        {
                            bend = std::find(b + 1, last, TYTI_L(charT, '\"'));
                            if (bend == last)
                            {
                                ec = std::make_error_code(std::errc::protocol_error);//could not find end of name
                                return root;
                            }

                            auto value = std::basic_string<charT>(b + 1, bend);
                            b = bend + 1;

                            if (curName != TYTI_L(charT, "#include") && curName != TYTI_L(charT, "#base"))
                                cur->attribs[curName] = value;
                            else
                            {
                                std::basic_ifstream<charT> i(detail::string_converter(value));
                                auto n = std::make_shared<basic_object<charT>>(read(i, ec));
                                if (ec) return root;
                                cur->childs[n->name] = n;
                            }
                        }
                        else if (*b == '{')
                        {
                            lvls.push(cur);
                            auto n = std::make_shared<basic_object<charT>>();
                            cur->childs[curName] = n;
                            cur = n.get();
                            cur->name = curName;
                            ++b;
                        }
                    }
                    else if (*b == '}')
                    {
                        cur = lvls.top();
                        lvls.pop();
                        ++b;
                    }
                }

            }
            catch (std::bad_alloc& )
            {
                ec = std::make_error_code(std::errc::not_enough_memory);
            }
            catch (...)
            {
                ec = std::make_error_code(std::errc::invalid_argument);
            }
            return root;
        }

        /** \brief Read VDF formatted sequences defined by the range [first, last).
        If the file is mailformatted, parser will try to read it until it can.
        @param first begin iterator
        @param end end iterator
        @param ok output bool. true, if parser successed, false, if parser failed
        */
        template<typename IterT, typename charT = typename IterT::value_type>
        basic_object<charT> read(IterT first, IterT last, bool* ok) noexcept
        {
            std::error_code ec;
            auto r = read(first, last, ec);
            if (ok) *ok = !ec;
            return r;
        }

        /** \brief Read VDF formatted sequences defined by the range [first, last).
        If the file is mailformatted, parser will try to read it until it can.
        @param first begin iterator
        @param end end iterator

        throws a "std::system_error" if a parsing error occured
        */
        template<typename IterT, typename charT = typename IterT::value_type>
        basic_object<charT> read(IterT first, IterT last)
        {
            std::error_code ec;
            const auto r = read(first, last, ec);
            if (!ec)
                throw std::system_error(ec);
            return r;
        }

        /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
            throws "std::bad_alloc" if file buffer could not be allocated
        */
        template<typename iStreamT, typename charT >
        basic_object<charT> read(iStreamT& inStream, std::error_code& ec)
        {
            // cache the file
            std::basic_string<charT> str;
            inStream.seekg(0, std::ios::end);
            str.resize(static_cast<size_t>(inStream.tellg()));
            if (str.empty())
                return basic_object<charT>();

            inStream.seekg(0, std::ios::beg);
            inStream.read(&str[0], str.size());
            inStream.close();

            // parse it
            return read(str.begin(), str.end(), ec);
        }

        /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
            throws "std::bad_alloc" if file buffer could not be allocated
            ok == false, if a parsing error occured
        */
        template<typename iStreamT, typename charT = typename iStreamT::char_type >
        basic_object<charT> read(iStreamT& inStream, bool* ok)
        {
            std::error_code ec;
            const auto r = read(inStream, ec);
            if (ok) *ok = !ec;
            return r;
        }

        /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
            throws "std::bad_alloc" if file buffer could not be allocated
            throws "std::system_error" if a parsing error occured
        */
        template<typename iStreamT, typename charT = typename iStreamT::char_type >
        basic_object<charT> read(iStreamT& inStream)
        {
            std::error_code ec;
            const auto r = read(inStream, ec);
            if (!ec) throw std::system_error(ec);
            return r;
        }

    } // end namespace vdf
} // end namespace tyti
#ifndef TYTI_NO_L_UNDEF
#undef TYTI_L
#endif

#endif //__TYTI_STEAM_VDF_PARSER_H__
<commit_msg>missing T<commit_after>//MIT License
//
//Copyright(c) 2016 Matthias Moeller
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files(the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.

#ifndef __TYTI_STEAM_VDF_PARSER_H__
#define __TYTI_STEAM_VDF_PARSER_H__

#include <unordered_map>
#include <utility>
#include <fstream>
#include <memory>

#include <system_error>
#include <exception>

//for wstring support
#include <locale>
#include <codecvt>
#include <string>

// internal
#include <stack>

namespace tyti
{
    namespace vdf
    {
        namespace detail
        {
            ///////////////////////////////////////////////////////////////////////////
            //  Helper functions selecting the right encoding (char/wchar_T)
            ///////////////////////////////////////////////////////////////////////////

            template<typename T>
            struct literal_macro_help
            {
                static constexpr const char* result(const char* c, const wchar_t* wc) noexcept
                {
                    return c;
                }
                static constexpr const char result(char c, wchar_t wc) noexcept
                {
                    return c;
                }
            };

            template<>
            struct literal_macro_help<wchar_t>
            {
                static constexpr const wchar_t* result(const char* c, const wchar_t* wc) noexcept
                {
                    return wc;
                }
                static constexpr const wchar_t result(char c, wchar_t wc) noexcept
                {
                    return wc;
                }
            };
#define TYTI_L(type, text) vdf::detail::literal_macro_help<type>::result(text, L##text)

            inline std::string string_converter(std::string& w)
            {
                return w;
            }

            inline std::string string_converter(std::wstring& w)
            {
                std::wstring_convert<std::codecvt_utf8<wchar_t>> conv1; // maybe wrong econding
                return conv1.to_bytes(w);
            }

            ///////////////////////////////////////////////////////////////////////////
            //  Writer helper functions
            ///////////////////////////////////////////////////////////////////////////

            struct tabs
            {
                size_t t;
                tabs(size_t i) :t(i) {}
            };

            template<typename oStreamT>
            oStreamT& operator<<(oStreamT& s, tabs t)
            {
                for (; t.t > 0; --t.t)
                    s << "\t";
                return s;
            }
        } // end namespace detail

        ///////////////////////////////////////////////////////////////////////////
        //  Interface
        ///////////////////////////////////////////////////////////////////////////

        /// basic object node. Every object has a name and can contains attributes saved as key_value pairs or childrens
        template<typename CharT>
        struct basic_object
        {
            typedef CharT char_type;
            std::basic_string<char_type> name;
            std::unordered_map<std::basic_string<char_type>, std::basic_string<char_type> > attribs;
            std::unordered_map<std::basic_string<char_type>, std::shared_ptr< basic_object<char_type> > > childs;
        };

        typedef basic_object<char> object;
        typedef basic_object<wchar_t> wobject;

        /** \brief writes given object tree in vdf format to given stream.
        Uses tabs instead of whitespaces.
        */
        template<typename oStreamT, typename charT = typename oStreamT::char_type>
        void write(oStreamT& s, const basic_object<charT>& r, size_t t = 0) noexcept(noexcept(std::operator<<(oStreamT&, const charT*)))
        {
            using namespace detail;
            s << tabs(t) << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tabs(t) << TYTI_L(charT, "{\n");
            for (auto& i : r.attribs)
                s << tabs(t + 1) << TYTI_L(charT, '"') << i.first << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n");
            for (auto& i : r.childs)
                write(s, i, t + 1);
            s << tabs(t) << TYTI_L(charT, "}\n");
        }

        //forward decls
        //forward decl
        template<typename iStreamT, typename charT = typename iStreamT::char_type >
        basic_object<charT> read(iStreamT& inStream, std::error_code& ec);

        /** \brief Read VDF formatted sequences defined by the range [first, last).
        If the file is mailformatted, parser will try to read it until it can.
        @param first begin iterator
        @param end end iterator
        @param ec output bool. 0 if ok, otherwise, holds an system error code

        Possible error codes:
            std::errc::protocol_error: file is mailformatted
            std::errc::not_enough_memory: not enough space
            std::errc::invalid_argument: iterators throws e.g. out of range
        */

        template<typename IterT, typename charT = typename IterT::value_type>
        basic_object<charT> read(IterT first, IterT last, std::error_code& ec) noexcept
        {
            ec.clear();

            basic_object<charT> root;
            basic_object<charT>* cur = &root;
            std::stack< basic_object<charT>* > lvls;
            //read header
            // first, quoted name
            auto b = std::find(first, last, TYTI_L(charT, '\"'));
            auto bend = std::find(b + 1, last, TYTI_L(charT, '\"'));
            root.name = std::basic_string<charT>(b + 1, bend);
            // second, get {}
            b = std::find(bend, last, TYTI_L(charT, '{'));
            try
            {
                if (b == last)
                {
                    ec = std::make_error_code(std::errc::protocol_error);
                    return root;
                }
                else
                    lvls.push(&root);
                while (!lvls.empty() && b != last)
                {
                    const std::basic_string<charT> startsym = TYTI_L(charT, "\"}");

                    //find first starting attrib/child, or ending
                    b = std::find_first_of(b, last, std::cbegin(startsym), std::cend(startsym));
                    if (*b == '\"')
                    {
                        bend = std::find(b + 1, last, TYTI_L(charT, '\"'));
                        if (bend == last)
                        {
                            ec = std::make_error_code(std::errc::protocol_error);// could not find end of name
                            return root;
                        }

                        std::basic_string<charT> curName(b + 1, bend);
                        b = bend + 1;

                        const std::basic_string<charT> ecspsym = TYTI_L(charT, "\"{");
                        b = std::find_first_of(b, last, std::cbegin(ecspsym), std::cend(ecspsym));
                        if (b == last)
                        {
                            ec = std::make_error_code(std::errc::protocol_error);// could not find 2nd part of pair
                            return root;
                        }

                        if (*b == '\"')
                        {
                            bend = std::find(b + 1, last, TYTI_L(charT, '\"'));
                            if (bend == last)
                            {
                                ec = std::make_error_code(std::errc::protocol_error);//could not find end of name
                                return root;
                            }

                            auto value = std::basic_string<charT>(b + 1, bend);
                            b = bend + 1;

                            if (curName != TYTI_L(charT, "#include") && curName != TYTI_L(charT, "#base"))
                                cur->attribs[curName] = value;
                            else
                            {
                                std::basic_ifstream<charT> i(detail::string_converter(value));
                                auto n = std::make_shared<basic_object<charT>>(read(i, ec));
                                if (ec) return root;
                                cur->childs[n->name] = n;
                            }
                        }
                        else if (*b == '{')
                        {
                            lvls.push(cur);
                            auto n = std::make_shared<basic_object<charT>>();
                            cur->childs[curName] = n;
                            cur = n.get();
                            cur->name = curName;
                            ++b;
                        }
                    }
                    else if (*b == '}')
                    {
                        cur = lvls.top();
                        lvls.pop();
                        ++b;
                    }
                }

            }
            catch (std::bad_alloc& )
            {
                ec = std::make_error_code(std::errc::not_enough_memory);
            }
            catch (...)
            {
                ec = std::make_error_code(std::errc::invalid_argument);
            }
            return root;
        }

        /** \brief Read VDF formatted sequences defined by the range [first, last).
        If the file is mailformatted, parser will try to read it until it can.
        @param first begin iterator
        @param end end iterator
        @param ok output bool. true, if parser successed, false, if parser failed
        */
        template<typename IterT, typename charT = typename IterT::value_type>
        basic_object<charT> read(IterT first, IterT last, bool* ok) noexcept
        {
            std::error_code ec;
            auto r = read(first, last, ec);
            if (ok) *ok = !ec;
            return r;
        }

        /** \brief Read VDF formatted sequences defined by the range [first, last).
        If the file is mailformatted, parser will try to read it until it can.
        @param first begin iterator
        @param end end iterator

        throws a "std::system_error" if a parsing error occured
        */
        template<typename IterT, typename charT = typename IterT::value_type>
        basic_object<charT> read(IterT first, IterT last)
        {
            std::error_code ec;
            const auto r = read(first, last, ec);
            if (!ec)
                throw std::system_error(ec);
            return r;
        }

        /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
            throws "std::bad_alloc" if file buffer could not be allocated
        */
        template<typename iStreamT, typename charT >
        basic_object<charT> read(iStreamT& inStream, std::error_code& ec)
        {
            // cache the file
            std::basic_string<charT> str;
            inStream.seekg(0, std::ios::end);
            str.resize(static_cast<size_t>(inStream.tellg()));
            if (str.empty())
                return basic_object<charT>();

            inStream.seekg(0, std::ios::beg);
            inStream.read(&str[0], str.size());
            inStream.close();

            // parse it
            return read(str.begin(), str.end(), ec);
        }

        /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
            throws "std::bad_alloc" if file buffer could not be allocated
            ok == false, if a parsing error occured
        */
        template<typename iStreamT, typename charT = typename iStreamT::char_type >
        basic_object<charT> read(iStreamT& inStream, bool* ok)
        {
            std::error_code ec;
            const auto r = read(inStream, ec);
            if (ok) *ok = !ec;
            return r;
        }

        /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
            throws "std::bad_alloc" if file buffer could not be allocated
            throws "std::system_error" if a parsing error occured
        */
        template<typename iStreamT, typename charT = typename iStreamT::char_type >
        basic_object<charT> read(iStreamT& inStream)
        {
            std::error_code ec;
            const auto r = read(inStream, ec);
            if (!ec) throw std::system_error(ec);
            return r;
        }

    } // end namespace vdf
} // end namespace tyti
#ifndef TYTI_NO_L_UNDEF
#undef TYTI_L
#endif

#endif //__TYTI_STEAM_VDF_PARSER_H__
<|endoftext|>


<commit_before>//===-- InstLoops.cpp ---------------------------------------- ---*- C++ -*--=//
// Pass to instrument loops
//
// At every backedge, insert a counter for that backedge and a call function
//===----------------------------------------------------------------------===//

#include "llvm/Reoptimizer/InstLoops.h"
#include "llvm/Support/CFG.h"
#include "llvm/Constants.h"
#include "llvm/iMemory.h"
#include "llvm/GlobalVariable.h"
#include "llvm/DerivedTypes.h"
#include "llvm/iOther.h"
#include "llvm/iOperators.h"
#include "llvm/iTerminators.h"
#include "llvm/iPHINode.h"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/Pass.h"

//this is used to color vertices
//during DFS

enum Color{
  WHITE,
  GREY,
  BLACK
};

struct InstLoops : public FunctionPass {
  bool runOnFunction(Function &F);
};

static RegisterOpt<InstLoops> X("instloops", "Instrument backedges for profiling");

// createInstLoopsPass - Create a new pass to add path profiling
//
Pass *createInstLoopsPass() {
  return new InstLoops();
}

//helper function to get back edges: it is called by
//the "getBackEdges" function below
void getBackEdgesVisit(BasicBlock *u,
                       std::map<BasicBlock *, Color > &color,
                       std::map<BasicBlock *, int > &d,
                       int &time, Value *threshold) {

  color[u]=GREY;
  time++;
  d[u]=time;

  for(BasicBlock::succ_iterator vl = succ_begin(u),
	ve = succ_end(u); vl != ve; ++vl){

    BasicBlock *BB = *vl;

    if(color[BB]!=GREY && color[BB]!=BLACK){
      getBackEdgesVisit(BB, color, d, time, threshold);
    }

    //now checking for d and f vals
    if(color[BB]==GREY){
      //so v is ancestor of u if time of u > time of v
      if(d[u] >= d[BB]){
        //insert a new basic block: modify terminator accordingly!
        BasicBlock *newBB = new BasicBlock("", u->getParent());
        BranchInst *ti = cast<BranchInst>(u->getTerminator());
        unsigned char index = 1;
        if(ti->getSuccessor(0) == BB){
          index = 0;
        }
        assert(ti->getNumSuccessors() > index && "Not enough successors!");
        ti->setSuccessor(index, newBB);

        //insert global variable of type int
        Constant *initializer = Constant::getNullValue(Type::IntTy);
        GlobalVariable *countVar = new GlobalVariable(Type::IntTy, false, true,
                                                      initializer,
                                                      "loopCounter",
                                                      u->getParent()->getParent());

        //load the variable
        Instruction *ldInst = new LoadInst(countVar,"");

        //increment
        Instruction *addIn =
          BinaryOperator::create(Instruction::Add, ldInst,
                                 ConstantSInt::get(Type::IntTy,1), "");

        //store
        Instruction *stInst = new StoreInst(addIn, countVar);

        Instruction *etr = new LoadInst(threshold, "threshold");
        Instruction *cmpInst = new SetCondInst(Instruction::SetLE, etr,
                                               addIn, "");

        BasicBlock *callTrigger = new BasicBlock("", u->getParent());
        //branch to calltrigger, or *vl
        Instruction *newBr = new BranchInst(callTrigger, BB, cmpInst);

        BasicBlock::InstListType &lt = newBB->getInstList();

        lt.push_back(ldInst);
        lt.push_back(addIn);
        lt.push_back(stInst);
        lt.push_back(etr);
        lt.push_back(cmpInst);
        lt.push_back(newBr);

        //Now add instructions to the triggerCall BB
        //now create a call function
        //call llvm_first_trigger(int *x);
        std::vector<const Type*> inCountArgs;
        inCountArgs.push_back(PointerType::get(Type::IntTy));

        const FunctionType *cFty = FunctionType::get(Type::VoidTy, inCountArgs,
                                                     false);
        Function *inCountMth =
          u->getParent()->getParent()->getOrInsertFunction("llvm_first_trigger", cFty);

        assert(inCountMth && "Initialize method could not be inserted!");

        std::vector<Value *> iniArgs;
        iniArgs.push_back(countVar);
        Instruction *call = new CallInst(inCountMth, iniArgs, "");
        callTrigger->getInstList().push_back(call);
        callTrigger->getInstList().push_back(new BranchInst(BB));

        //now iterate over *vl, and set its Phi nodes right
        for(BasicBlock::iterator BB2Inst = BB->begin(), BBend = BB->end();
            BB2Inst != BBend; ++BB2Inst){

          if (PHINode *phiInst = dyn_cast<PHINode>(BB2Inst)){
            int bbIndex = phiInst->getBasicBlockIndex(u);
            if(bbIndex>=0){
              phiInst->setIncomingBlock(bbIndex, newBB);

              Value *val = phiInst->getIncomingValue((unsigned int)bbIndex);
              phiInst->addIncoming(val, callTrigger);
            }
          }
        }
      }
    }
  }
  color[u]=BLACK;//done with visiting the node and its neighbors
}

//getting the backedges in a graph
//Its a variation of DFS to get the backedges in the graph
//We get back edges by associating a time
//and a color with each vertex.
//The time of a vertex is the time when it was first visited
//The color of a vertex is initially WHITE,
//Changes to GREY when it is first visited,
//and changes to BLACK when ALL its neighbors
//have been visited
//So we have a back edge when we meet a successor of
//a node with smaller time, and GREY color
void getBackEdges(Function &F, Value *threshold){
  std::map<BasicBlock *, Color > color;
  std::map<BasicBlock *, int> d;
  int time=0;
  getBackEdgesVisit(F.begin(), color, d, time, threshold);
}

//Per function pass for inserting counters and call function
bool InstLoops::runOnFunction(Function &F){

  static GlobalVariable *threshold = NULL;
  static bool insertedThreshold = false;

   if(!insertedThreshold){
    threshold = new GlobalVariable(Type::IntTy, false, false, 0,
                                                   "reopt_threshold");

    F.getParent()->getGlobalList().push_back(threshold);
    insertedThreshold = true;
  }

  if(F.getName() == "main"){
    //intialize threshold
    std::vector<const Type*> initialize_args;
    initialize_args.push_back(PointerType::get(Type::IntTy));

    const FunctionType *Fty = FunctionType::get(Type::VoidTy, initialize_args,
                                                false);
    Function *initialMeth = F.getParent()->getOrInsertFunction("reoptimizerInitialize", Fty);
    assert(initialMeth && "Initialize method could not be inserted!");

    std::vector<Value *> trargs;
    trargs.push_back(threshold);

    new CallInst(initialMeth, trargs, "", F.begin()->begin());
  }

  assert(threshold && "GlobalVariable threshold not defined!");

  if(F.isExternal()) {
    return false;
  }

  getBackEdges(F, threshold);

  return true;
}
<commit_msg>Added functionality to instrmentation pass<commit_after>//===-- InstLoops.cpp ---------------------------------------- ---*- C++ -*--=//
// Pass to instrument loops
//
// At every backedge, insert a counter for that backedge and a call function
//===----------------------------------------------------------------------===//

#include "llvm/Reoptimizer/InstLoops.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/Support/CFG.h"
#include "llvm/Constants.h"
#include "llvm/iMemory.h"
#include "llvm/GlobalVariable.h"
#include "llvm/DerivedTypes.h"
#include "llvm/iOther.h"
#include "llvm/iOperators.h"
#include "llvm/iTerminators.h"
#include "llvm/iPHINode.h"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/Pass.h"

//this is used to color vertices
//during DFS

enum Color{
  WHITE,
  GREY,
  BLACK
};

namespace{
  struct InstLoops : public FunctionPass {
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.addRequired<DominatorSet>();
    }
  private:
    DominatorSet *DS;
    void getBackEdgesVisit(BasicBlock *u,
			   std::map<BasicBlock *, Color > &color,
			   std::map<BasicBlock *, int > &d,
			   int &time, Value *threshold,
			   std::map<BasicBlock *, BasicBlock *> &be);
    void removeRedundant(std::map<BasicBlock *, BasicBlock *> &be);
    void getBackEdges(Function &F, Value *threshold);
  public:
    bool runOnFunction(Function &F);
  };

    RegisterOpt<InstLoops> X("instloops", "Instrument backedges for profiling");
}

// createInstLoopsPass - Create a new pass to add path profiling
//
Pass *createInstLoopsPass() {
  return new InstLoops();
}

//helper function to get back edges: it is called by
//the "getBackEdges" function below
void InstLoops::getBackEdgesVisit(BasicBlock *u,
                       std::map<BasicBlock *, Color > &color,
                       std::map<BasicBlock *, int > &d,
                       int &time, Value *threshold,
		       std::map<BasicBlock *, BasicBlock *> &be) {

  color[u]=GREY;
  time++;
  d[u]=time;

  for(BasicBlock::succ_iterator vl = succ_begin(u),
	ve = succ_end(u); vl != ve; ++vl){

    BasicBlock *BB = *vl;

    if(color[BB]!=GREY && color[BB]!=BLACK){
      getBackEdgesVisit(BB, color, d, time, threshold, be);
    }

    //now checking for d and f vals
    else if(color[BB]==GREY){
      //so v is ancestor of u if time of u > time of v
      if(d[u] >= d[BB]){
	//u->BB is a backedge
	be[u] = BB;
      }
    }
  }
  color[u]=BLACK;//done with visiting the node and its neighbors
}

//look at all BEs, and remove all BEs that are dominated by other BE's in the
//set
void InstLoops::removeRedundant(std::map<BasicBlock *, BasicBlock *> &be){
  std::vector<BasicBlock *> toDelete;
  for(std::map<BasicBlock *, BasicBlock *>::iterator MI = be.begin(),
	ME = be.end(); MI != ME; ++MI){
    //std::cerr<<MI->first->getName()<<"\t->\t"<<MI->second->getName()<<"\n";
    //std::cerr<<MI->first;
    //std::cerr<<MI->second;
    for(std::map<BasicBlock *, BasicBlock *>::iterator MMI = be.begin(),
	  MME = be.end(); MMI != MME; ++MMI){
      if(DS->properlyDominates(MI->first, MMI->first)){
	toDelete.push_back(MMI->first);
	//std::cerr<<MI->first->getName()<<"\t Dominates\t"<<MMI->first->getName();
      }
    }
  }

  for(std::vector<BasicBlock *>::iterator VI = toDelete.begin(),
	VE = toDelete.end(); VI != VE; ++VI){
    be.erase(*VI);
  }
}

//getting the backedges in a graph
//Its a variation of DFS to get the backedges in the graph
//We get back edges by associating a time
//and a color with each vertex.
//The time of a vertex is the time when it was first visited
//The color of a vertex is initially WHITE,
//Changes to GREY when it is first visited,
//and changes to BLACK when ALL its neighbors
//have been visited
//So we have a back edge when we meet a successor of
//a node with smaller time, and GREY color
void InstLoops::getBackEdges(Function &F, Value *threshold){
  std::map<BasicBlock *, Color > color;
  std::map<BasicBlock *, int> d;
  std::map<BasicBlock *, BasicBlock *> be;
  int time=0;
  getBackEdgesVisit(F.begin(), color, d, time, threshold, be);

  removeRedundant(be);

  for(std::map<BasicBlock *, BasicBlock *>::iterator MI = be.begin(),
	ME = be.end(); MI != ME; ++MI){
    BasicBlock *u = MI->first;
    BasicBlock *BB = MI->second;
    //std::cerr<<"Edge from: "<<BB->getName()<<"->"<<u->getName()<<"\n";
    //insert a new basic block: modify terminator accordingly!
    BasicBlock *newBB = new BasicBlock("", u->getParent());
    BranchInst *ti = cast<BranchInst>(u->getTerminator());
    unsigned char index = 1;
    if(ti->getSuccessor(0) == BB){
      index = 0;
    }
    assert(ti->getNumSuccessors() > index && "Not enough successors!");
    ti->setSuccessor(index, newBB);

    BasicBlock::InstListType &lt = newBB->getInstList();

    std::vector<const Type*> inCountArgs;
    const FunctionType *cFty = FunctionType::get(Type::VoidTy, inCountArgs,
						 false);
    Function *inCountMth =
      u->getParent()->getParent()->getOrInsertFunction("llvm_first_trigger",
						       cFty);

    assert(inCountMth && "Initial method could not be inserted!");

    Instruction *call = new CallInst(inCountMth, "");
    lt.push_back(call);
    lt.push_back(new BranchInst(BB));

    //now iterate over *vl, and set its Phi nodes right
    for(BasicBlock::iterator BB2Inst = BB->begin(), BBend = BB->end();
	BB2Inst != BBend; ++BB2Inst){

      if (PHINode *phiInst = dyn_cast<PHINode>(BB2Inst)){
	int bbIndex = phiInst->getBasicBlockIndex(u);
	if(bbIndex>=0){
	  phiInst->setIncomingBlock(bbIndex, newBB);
	}
      }
    }
  }
}

//Per function pass for inserting counters and call function
bool InstLoops::runOnFunction(Function &F){

  static GlobalVariable *threshold = NULL;
  static bool insertedThreshold = false;

  DS  = &getAnalysis<DominatorSet>();

  if(F.isExternal()) {
    return false;
  }

  if(!insertedThreshold){
    threshold = new GlobalVariable(Type::IntTy, false,
				   GlobalValue::ExternalLinkage, 0,
                                   "reopt_threshold");

    F.getParent()->getGlobalList().push_back(threshold);
    insertedThreshold = true;
  }

  if(F.getName() == "main"){
    //intialize threshold
    std::vector<const Type*> initialize_args;
    initialize_args.push_back(PointerType::get(Type::IntTy));

    const FunctionType *Fty = FunctionType::get(Type::VoidTy, initialize_args,
                                                false);
    Function *initialMeth = F.getParent()->getOrInsertFunction("reoptimizerInitialize", Fty);
    assert(initialMeth && "Initialize method could not be inserted!");

    std::vector<Value *> trargs;
    trargs.push_back(threshold);

    new CallInst(initialMeth, trargs, "", F.begin()->begin());
  }

  assert(threshold && "GlobalVariable threshold not defined!");

  getBackEdges(F, threshold);

  return true;
}
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */
#include <crossbow/program_options.hpp>
#include <crossbow/logger.hpp>
#include <crossbow/allocator.hpp>

#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <string>
#include <iostream>
#include <cassert>
#include <fstream>

#include <thread>

#include <common/Util.hpp>

#include "Client.hpp"

using namespace crossbow::program_options;
using namespace boost::asio;
using err_code = boost::system::error_code;

int main(int argc, const char** argv) {
    bool help = false;
    bool populate = false;
    crossbow::string host;
    std::string port("8713");
    std::string logLevel("DEBUG");
    std::string outFile("out.csv");
    size_t numClients = 1;
    std::string baseDir = "/mnt/SG/braunl-tpch-data/all/1";
    uint batchSize = 1500;
    unsigned time = 5*60;
    bool exit = false;
    auto opts = create_options("tpch_client",
            value<'h'>("help", &help, tag::description{"print help"})
            , value<'H'>("host", &host, tag::description{"Comma-separated list of hosts"})
            , value<'l'>("log-level", &logLevel, tag::description{"The log level"})
            , value<'c'>("num-clients", &numClients, tag::description{"Number of Clients to run per host"})
            , value<'P'>("populate", &populate, tag::description{"Populate the database"})
            , value<'t'>("time", &time, tag::description{"Duration of the benchmark in seconds"})
            , value<'o'>("out", &outFile, tag::description{"Path to the output file"})
            , value<'d'>("base-dir", &baseDir, tag::description{"Base directory to the generated tbl/upd/del files, assumes for population that this base-dir exists on server as well."})
            , value<'b'>("batch-size", &batchSize, tag::description{"Batch Size for RF1/RF2 to be logged."})
            , value<-1>("exit", &exit, tag::description{"Quit server"})
            );
    try {
        parse(opts, argc, argv);
    } catch (argument_not_found& e) {
        std::cerr << e.what() << std::endl << std::endl;
        print_help(std::cout, opts);
        return 1;
    }
    if (help) {
        print_help(std::cout, opts);
        return 0;
    }

    crossbow::allocator::init();

    if (host.empty()) {
        std::cerr << "No host\n";
        return 1;
    }
    auto startTime = tpch::Clock::now();
    auto endTime = startTime + std::chrono::seconds(time);
    crossbow::logger::logger->config.level = crossbow::logger::logLevelFromString(logLevel);
    try {
        auto hosts = tpch::split(host.c_str(), ',');
        io_service service;

        // do client creation multi-threaded as it may take a while
        LOG_DEBUG("Start client creation.");
        auto sumClients = hosts.size() * numClients;
        std::vector<tpch::Client> clients;
        clients.reserve(sumClients);
        for (decltype(sumClients) i = 0; i < sumClients; ++i) {
            clients.emplace_back(service, endTime, batchSize);
        }
        LOG_DEBUG("Client creation finished.");

        for (size_t i = 0; i < hosts.size(); ++i) {
            auto h = hosts[i];
            auto addr = tpch::split(h, ':');
            assert(addr.size() <= 2);
            auto p = addr.size() == 2 ? addr[1] : port;
            ip::tcp::resolver resolver(service);
            ip::tcp::resolver::iterator iter;
            if (host == "") {
                iter = resolver.resolve(ip::tcp::resolver::query(p));
            } else {
                iter = resolver.resolve(ip::tcp::resolver::query(addr[0], p));
            }
            for (unsigned j = 0; j < numClients; ++j) {
                LOG_INFO("Connected to client " + crossbow::to_string(i*numClients + j));
                boost::asio::connect(clients[i*numClients + j].socket(), iter);
            }
        }

        {
            auto t = std::time(nullptr);
            std::string dateString(20, '\0');
            auto len = std::strftime(&dateString.front(), 20, "%T", std::localtime(&t));
            dateString.resize(len);
            LOG_INFO("Starting client at %1%", dateString);
        }

        if (exit) {
            for (auto& client : clients) {
                auto& cmds = client.commands();
                cmds.execute<tpch::Command::EXIT>([](const err_code& ec){
                    if (ec) {
                        std::cerr << "ERROR: " << ec.message() << std::endl;
                    }
                });
                goto END;
            }
        }

        if (populate) {
            auto& cmds = clients[0].commands();
            double scalingFactor = tpch::getScalingFactor(baseDir);
            cmds.execute<tpch::Command::CREATE_SCHEMA>(
                    [&clients, &baseDir, &scalingFactor](const err_code& ec,
                        const std::tuple<bool, crossbow::string>& res){
                if (ec) {
                    LOG_ERROR(ec.message());
                    return;
                }
                if (!std::get<0>(res)) {
                    LOG_ERROR(std::get<1>(res));
                    return;
                }

                auto& cmds = clients[0].commands();
                // populates regions and nations, and other tables if they are not split
                cmds.execute<tpch::Command::POPULATE>([&clients, &baseDir, &scalingFactor](const err_code& ec, const std::tuple<bool, crossbow::string>& res){
                    if (ec) {
                        LOG_ERROR(ec.message());
                        return;
                    }
                    if (!std::get<0>(res)) {
                        LOG_ERROR(std::get<1>(res));
                        return;
                    }

                    // populates split files of other tables
                    for (uint32_t i = 0; i < clients.size(); ++i) {
                        clients[i].populate(baseDir, i);
                    }
                }, std::make_pair(uint32_t(0), crossbow::string(baseDir)));
            }, scalingFactor);
        } else {
            std::vector<std::thread> threads;
            threads.reserve(threads.size());
            for (uint32_t i = 0; i < clients.size(); ++i) {
                threads.emplace_back([&, i]{
                    clients[i].prepare(baseDir, uint(i));
                });
            }

            for (auto &thread: threads) {
                thread.join();
            }

            for (auto& client : clients) {
                client.run();
            }
        }
END:
        service.run();
        LOG_INFO("Done, writing results");
        std::ofstream out(outFile.c_str());
        out << "start,end,transaction,success,error\n";
        for (const auto& client : clients) {
            const auto& queue = client.log();
            for (const auto& e : queue) {
                crossbow::string tName;
                switch (e.transaction) {
                case tpch::Command::CREATE_SCHEMA:
                    tName = "Create schema";
                    break;
                case tpch::Command::POPULATE:
                    tName = "Populate remaining tables";
                    break;
                case tpch::Command::RF1:
                    tName = "RF1";
                    break;
                case tpch::Command::RF2:
                    tName = "RF2";
                    break;
                case tpch::Command::EXIT:
                    assert(false);
                    break;
                }
                out << std::chrono::duration_cast<std::chrono::milliseconds>(e.start - startTime).count() << ','
                    << std::chrono::duration_cast<std::chrono::milliseconds>(e.end - startTime).count() << ','
                    << tName << ','
                    << (e.success ? "true" : "false") << ','
                    << e.error << std::endl;
            }
        }
        std::cout << '\a';
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
    return 0;
}
<commit_msg>fixed population bug<commit_after>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */
#include <crossbow/program_options.hpp>
#include <crossbow/logger.hpp>
#include <crossbow/allocator.hpp>

#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <string>
#include <iostream>
#include <cassert>
#include <fstream>

#include <thread>

#include <common/Util.hpp>

#include "Client.hpp"

using namespace crossbow::program_options;
using namespace boost::asio;
using err_code = boost::system::error_code;

int main(int argc, const char** argv) {
    bool help = false;
    bool populate = false;
    crossbow::string host;
    std::string port("8713");
    std::string logLevel("DEBUG");
    std::string outFile("out.csv");
    size_t numClients = 1;
    std::string baseDir = "/mnt/SG/braunl-tpch-data/all/1";
    uint batchSize = 1500;
    unsigned time = 5*60;
    bool exit = false;
    auto opts = create_options("tpch_client",
            value<'h'>("help", &help, tag::description{"print help"})
            , value<'H'>("host", &host, tag::description{"Comma-separated list of hosts"})
            , value<'l'>("log-level", &logLevel, tag::description{"The log level"})
            , value<'c'>("num-clients", &numClients, tag::description{"Number of Clients to run per host"})
            , value<'P'>("populate", &populate, tag::description{"Populate the database"})
            , value<'t'>("time", &time, tag::description{"Duration of the benchmark in seconds"})
            , value<'o'>("out", &outFile, tag::description{"Path to the output file"})
            , value<'d'>("base-dir", &baseDir, tag::description{"Base directory to the generated tbl/upd/del files, assumes for population that this base-dir exists on server as well."})
            , value<'b'>("batch-size", &batchSize, tag::description{"Batch Size for RF1/RF2 to be logged."})
            , value<-1>("exit", &exit, tag::description{"Quit server"})
            );
    try {
        parse(opts, argc, argv);
    } catch (argument_not_found& e) {
        std::cerr << e.what() << std::endl << std::endl;
        print_help(std::cout, opts);
        return 1;
    }
    if (help) {
        print_help(std::cout, opts);
        return 0;
    }

    crossbow::allocator::init();

    if (host.empty()) {
        std::cerr << "No host\n";
        return 1;
    }
    auto startTime = tpch::Clock::now();
    auto endTime = startTime + std::chrono::seconds(time);
    crossbow::logger::logger->config.level = crossbow::logger::logLevelFromString(logLevel);
    try {
        auto hosts = tpch::split(host.c_str(), ',');
        io_service service;

        // do client creation multi-threaded as it may take a while
        LOG_DEBUG("Start client creation.");
        auto sumClients = hosts.size() * numClients;
        std::vector<std::unique_ptr<tpch::Client>> clients;
        clients.reserve(sumClients);
        for (decltype(sumClients) i = 0; i < sumClients; ++i) {
            clients.emplace_back(new tpch::Client(service, endTime, batchSize));
        }
        LOG_DEBUG("Client creation finished.");

        for (size_t i = 0; i < hosts.size(); ++i) {
            auto h = hosts[i];
            auto addr = tpch::split(h, ':');
            assert(addr.size() <= 2);
            auto p = addr.size() == 2 ? addr[1] : port;
            ip::tcp::resolver resolver(service);
            ip::tcp::resolver::iterator iter;
            if (host == "") {
                iter = resolver.resolve(ip::tcp::resolver::query(p));
            } else {
                iter = resolver.resolve(ip::tcp::resolver::query(addr[0], p));
            }
            for (unsigned j = 0; j < numClients; ++j) {
                LOG_INFO("Connected to client " + crossbow::to_string(i*numClients + j));
                boost::asio::connect(clients[i*numClients + j]->socket(), iter);
            }
        }

        {
            auto t = std::time(nullptr);
            std::string dateString(20, '\0');
            auto len = std::strftime(&dateString.front(), 20, "%T", std::localtime(&t));
            dateString.resize(len);
            LOG_INFO("Starting client at %1%", dateString);
        }

        if (exit) {
            for (auto& client : clients) {
                auto& cmds = client->commands();
                cmds.execute<tpch::Command::EXIT>([](const err_code& ec){
                    if (ec) {
                        std::cerr << "ERROR: " << ec.message() << std::endl;
                    }
                });
                goto END;
            }
        }

        if (populate) {
            auto& cmds = clients[0]->commands();
            double scalingFactor = tpch::getScalingFactor(baseDir);
            cmds.execute<tpch::Command::CREATE_SCHEMA>(
                    [&clients, &baseDir, &scalingFactor](const err_code& ec,
                        const std::tuple<bool, crossbow::string>& res){
                if (ec) {
                    LOG_ERROR(ec.message());
                    return;
                }
                if (!std::get<0>(res)) {
                    LOG_ERROR(std::get<1>(res));
                    return;
                }

                auto& cmds = clients[0]->commands();
                // populates regions and nations, and other tables if they are not split
                cmds.execute<tpch::Command::POPULATE>([&clients, &baseDir, &scalingFactor](const err_code& ec, const std::tuple<bool, crossbow::string>& res){
                    if (ec) {
                        LOG_ERROR(ec.message());
                        return;
                    }
                    if (!std::get<0>(res)) {
                        LOG_ERROR(std::get<1>(res));
                        return;
                    }

                    // populates split files of other tables
                    for (uint32_t i = 0; i < clients.size(); ++i) {
                        clients[i]->populate(baseDir, i);
                    }
                }, std::make_pair(uint32_t(0), crossbow::string(baseDir)));
            }, scalingFactor);
        } else {
            std::vector<std::thread> threads;
            threads.reserve(threads.size());
            for (uint32_t i = 0; i < clients.size(); ++i) {
                threads.emplace_back([&, i]{
                    clients[i]->prepare(baseDir, uint(i));
                });
            }

            for (auto &thread: threads) {
                thread.join();
            }

            for (auto& client : clients) {
                client->run();
            }
        }
END:
        service.run();
        LOG_INFO("Done, writing results");
        std::ofstream out(outFile.c_str());
        out << "start,end,transaction,success,error\n";
        for (const auto& client : clients) {
            const auto& queue = client->log();
            for (const auto& e : queue) {
                crossbow::string tName;
                switch (e.transaction) {
                case tpch::Command::CREATE_SCHEMA:
                    tName = "Create schema";
                    break;
                case tpch::Command::POPULATE:
                    tName = "Populate remaining tables";
                    break;
                case tpch::Command::RF1:
                    tName = "RF1";
                    break;
                case tpch::Command::RF2:
                    tName = "RF2";
                    break;
                case tpch::Command::EXIT:
                    assert(false);
                    break;
                }
                out << std::chrono::duration_cast<std::chrono::milliseconds>(e.start - startTime).count() << ','
                    << std::chrono::duration_cast<std::chrono::milliseconds>(e.end - startTime).count() << ','
                    << tName << ','
                    << (e.success ? "true" : "false") << ','
                    << e.error << std::endl;
            }
        }
        std::cout << '\a';
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
    return 0;
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#include "vast/command.hpp"

#include <numeric>

#include <caf/make_message.hpp>
#include <caf/message.hpp>

#include "vast/detail/string.hpp"
#include "vast/error.hpp"
#include "vast/logger.hpp"

namespace vast {

caf::config_option_set command::opts() {
  return caf::config_option_set{}.add<bool>("help,h?", "prints the help text");
}

command* command::add(fun child_run, std::string_view child_name,
                      std::string_view child_description,
                      caf::config_option_set child_options) {
  return children
    .emplace_back(new command{this,
                              child_run,
                              child_name,
                              child_description,
                              std::move(child_options),
                              {}})
    .get();
}

caf::message run(const command& cmd, caf::actor_system& sys,
                 caf::config_value_map& options,
                 command::argument_iterator first,
                 command::argument_iterator last) {
  using caf::get_or;
  using caf::make_message;
  VAST_TRACE(VAST_ARG(std::string(name_)), VAST_ARG("args", first, last),
             VAST_ARG(options));
  // Parse arguments for this command.
  auto [state, position] = cmd.options.parse(options, first, last);
  bool has_subcommand;
  switch(state) {
    default:
      return make_message(make_error(ec::unrecognized_option, *position));
    case caf::pec::success:
      has_subcommand = false;
      break;
    case caf::pec::not_an_option:
      has_subcommand = position != last;
      break;
  }
  if (position != last && detail::starts_with(*position, "-"))
    return make_message(make_error(ec::unrecognized_option, *position));
  // Check for help option.
  if (get_or<bool>(options, "help", false)) {
    helptext(cmd, std::cerr);
    return caf::none;
  }
  // Check for version option.
  if (get_or<bool>(options, "version", false)) {
    std::cerr << VAST_VERSION << std::endl;
    return caf::none;
  }
  // Invoke cmd.run if no subcommand was defined.
  if (!has_subcommand) {
    // Commands without a run implementation require subcommands.
    if (cmd.run == nullptr)
      return make_message(make_error(ec::missing_subcommand));
    return cmd.run(cmd, sys, options, position, last);
  }
  // Consume CLI arguments if we have arguments but don't have subcommands.
  if (cmd.children.empty())
    return cmd.run(cmd, sys, options, position, last);
  // Dispatch to subcommand.
  // TODO: We need to copy the iterator here, because structured binding cannot
  //       be captured. Clang reports the error "reference to local binding
  //       'position' declared in enclosing function 'vast::command::run'" when
  //       trying to use the structured binding inside the lambda.
  //       See also: https://stackoverflow.com/questions/46114214. Remove this
  //       workaround when all supported compilers accept accessing structured
  //       bindings from lambdas.
  auto i = std::find_if(cmd.children.begin(), cmd.children.end(),
                        [p = position](auto& x) { return x->name == *p; });
  if (i == cmd.children.end())
    return make_message(make_error(ec::invalid_subcommand, *position));
  return run(**i, sys, options, position + 1, last);
}

caf::message run(const command& cmd, caf::actor_system& sys,
                 command::argument_iterator first,
                 command::argument_iterator last) {
  caf::config_value_map options;
  return run(cmd, sys, options, first, last);
}

std::string full_name(const command& cmd) {
  std::string result{cmd.name};
  for (auto ptr = cmd.parent; ptr != nullptr; ptr = ptr->parent) {
    result.insert(result.begin(), ' ');
    result.insert(result.begin(), ptr->name.begin(), ptr->name.end());
  }
  return result;
}

namespace {

using std::accumulate;

// Returns the field size for printing all names in `xs`.
auto field_size(const command::children_list& xs) {
  return accumulate(xs.begin(), xs.end(), size_t{0}, [](size_t x, auto& y) {
    return std::max(x, y->name.size());
  });
}

// Returns the field size for printing all names in `xs`.
auto field_size(const caf::config_option_set& xs) {
  return accumulate(xs.begin(), xs.end(), size_t{0}, [](size_t x, auto& y) {
    // We print parameters in the form "[-h | -? | --help]". So, "[]" adds 2
    // characters, each short name adds 5 characters with "-X | " and the
    // long name finally gets the prefix "--".
    return std::max(x, 4 + (y.short_names().size() * 5) + y.long_name().size());
  });
}

void parameters_helptext(const command& cmd, std::ostream& out) {
  out << "parameters:\n";
  auto fs = field_size(cmd.options);
  for (auto& opt : cmd.options) {
    out << "  ";
    std::string lst = "[";
    for (auto ch : opt.short_names()) {
      lst += '-';
      lst += ch;
      lst += " | ";
    }
    lst += "--";
    lst.insert(lst.end(), opt.long_name().begin(), opt.long_name().end());
    lst += ']';
    out.width(fs);
    out << lst << "  " << opt.description() << '\n';
  }
}

// Prints the helptext for a command without children.
void flat_helptext(const command& cmd, std::ostream& out) {
  // A trivial command without parameters prints its name and description.
  if (cmd.options.empty()) {
    out << "usage: " << full_name(cmd) << "\n\n"
        << cmd.description << "\n\n";
    return;
  }
  // A command with parameters prints 1) its name, 2) a description, and 3) a
  // list of available parameters.
  out << "usage: " << full_name(cmd) << " [<parameters>]\n\n"
      << cmd.description << "\n\n";
  parameters_helptext(cmd, out);
}

void subcommand_helptext(const command& cmd, std::ostream& out) {
  out << "subcommands:\n";
  auto fs = field_size(cmd.children);
  for (auto& child : cmd.children) {
    out << "  ";
    out.width(fs);
    out << child->name << "  " << child->description << '\n';
  }
}

// Prints the helptext for a command without children.
void nested_helptext(const command& cmd, std::ostream& out) {
  // A trivial command without parameters prints name, description and
  // children.
  if (cmd.options.empty()) {
    out << "usage: " << full_name(cmd) << " <command>" << "\n\n"
        << cmd.description  << "\n\n";
    subcommand_helptext(cmd, out);
    return;
  }
  out << "usage: " << full_name(cmd) << " [<parameters>] <command>" << "\n\n"
      << cmd.description << "\n\n";
  parameters_helptext(cmd, out);
  out << '\n';
  subcommand_helptext(cmd, out);
}

} // namespace <anonymous>

void helptext(const command& cmd, std::ostream& out) {
  // Make sure fields are filled left-to-right.
  out << std::left;
  // Dispatch based on whether or nood cmd has children.
  if (cmd.children.empty())
    flat_helptext(cmd, out);
  else
    nested_helptext(cmd, out);
}

std::string helptext(const command& cmd) {
  std::ostringstream oss;
  helptext(cmd, oss);
  return oss.str();
}

/*

std::string command::usage() const {
  std::stringstream result;
  result << opts_.help_text();
  if (!children_.empty()) {
    result << "\nsubcommands:\n";
    for (auto& child : children_)
      result << "  " << child->name() << "\n";
  }
  return result.str();
}

std::string command::full_name() const {
  std::string result;
  if (is_root())
    return result;
  result = parent_->full_name();
  if (!result.empty())
    result += ' ';
  result += name_;
  return result;
}

caf::error command::proceed(caf::actor_system&,
                            const caf::config_value_map& options,
                            argument_iterator begin, argument_iterator end) {
  VAST_UNUSED(options, begin, end);
  VAST_TRACE(VAST_ARG(std::string{name_}), VAST_ARG("args", begin, end),
             VAST_ARG(options));
  return caf::none;
}

caf::message command::run_impl(caf::actor_system&,
                               const caf::config_value_map& options,
                               argument_iterator begin, argument_iterator end) {
  VAST_UNUSED(options, begin, end);
  VAST_TRACE(VAST_ARG(std::string{name_}), VAST_ARG("args", begin, end),
             VAST_ARG(options));
  usage();
  return caf::none;
}

caf::message command::wrap_error(caf::error err) {
  return caf::make_message(std::move(err));
}

caf::error command::parse_error(caf::pec code, argument_iterator error_position,
                                argument_iterator begin,
                                argument_iterator end) const {
  std::stringstream result;
  result << "Failed to parse command" << "\n";
  result << "  Command: " << name() << " " << detail::join(begin, end, " ")
         << "\n";
  result << "  Description: " << to_string(code) << "\n";
  if (error_position != end)
    result << "  Position: " << *error_position << "";
  else
    result << "  Position: at the end";
  return make_error(ec::parse_error, result.str());
}

caf::error command::unknown_subcommand_error(argument_iterator error_position,
                                             argument_iterator end) const {
  VAST_ASSERT(error_position != end);
  auto& str = *error_position;
  std::string helptext;
  if (!str.empty() && str.front() == '-') {
    helptext = "invalid option: ";
    helptext += str;
  } else {
    helptext = "unknown command: ";
    if (!is_root()) {
      helptext += full_name();
      helptext += ' ';
    }
    helptext += str;
  }
  return make_error(ec::syntax_error, std::move(helptext));
}
*/

} // namespace vast
<commit_msg>Include argument type for parameters<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#include "vast/command.hpp"

#include <numeric>

#include <caf/make_message.hpp>
#include <caf/message.hpp>

#include "vast/detail/string.hpp"
#include "vast/error.hpp"
#include "vast/logger.hpp"

namespace vast {

caf::config_option_set command::opts() {
  return caf::config_option_set{}.add<bool>("help,h?", "prints the help text");
}

command* command::add(fun child_run, std::string_view child_name,
                      std::string_view child_description,
                      caf::config_option_set child_options) {
  return children
    .emplace_back(new command{this,
                              child_run,
                              child_name,
                              child_description,
                              std::move(child_options),
                              {}})
    .get();
}

caf::message run(const command& cmd, caf::actor_system& sys,
                 caf::config_value_map& options,
                 command::argument_iterator first,
                 command::argument_iterator last) {
  using caf::get_or;
  using caf::make_message;
  VAST_TRACE(VAST_ARG(std::string(name_)), VAST_ARG("args", first, last),
             VAST_ARG(options));
  // Parse arguments for this command.
  auto [state, position] = cmd.options.parse(options, first, last);
  bool has_subcommand;
  switch(state) {
    default:
      return make_message(make_error(ec::unrecognized_option, *position));
    case caf::pec::success:
      has_subcommand = false;
      break;
    case caf::pec::not_an_option:
      has_subcommand = position != last;
      break;
  }
  if (position != last && detail::starts_with(*position, "-"))
    return make_message(make_error(ec::unrecognized_option, *position));
  // Check for help option.
  if (get_or<bool>(options, "help", false)) {
    helptext(cmd, std::cerr);
    return caf::none;
  }
  // Check for version option.
  if (get_or<bool>(options, "version", false)) {
    std::cerr << VAST_VERSION << std::endl;
    return caf::none;
  }
  // Invoke cmd.run if no subcommand was defined.
  if (!has_subcommand) {
    // Commands without a run implementation require subcommands.
    if (cmd.run == nullptr)
      return make_message(make_error(ec::missing_subcommand));
    return cmd.run(cmd, sys, options, position, last);
  }
  // Consume CLI arguments if we have arguments but don't have subcommands.
  if (cmd.children.empty())
    return cmd.run(cmd, sys, options, position, last);
  // Dispatch to subcommand.
  // TODO: We need to copy the iterator here, because structured binding cannot
  //       be captured. Clang reports the error "reference to local binding
  //       'position' declared in enclosing function 'vast::command::run'" when
  //       trying to use the structured binding inside the lambda.
  //       See also: https://stackoverflow.com/questions/46114214. Remove this
  //       workaround when all supported compilers accept accessing structured
  //       bindings from lambdas.
  auto i = std::find_if(cmd.children.begin(), cmd.children.end(),
                        [p = position](auto& x) { return x->name == *p; });
  if (i == cmd.children.end())
    return make_message(make_error(ec::invalid_subcommand, *position));
  return run(**i, sys, options, position + 1, last);
}

caf::message run(const command& cmd, caf::actor_system& sys,
                 command::argument_iterator first,
                 command::argument_iterator last) {
  caf::config_value_map options;
  return run(cmd, sys, options, first, last);
}

std::string full_name(const command& cmd) {
  std::string result{cmd.name};
  for (auto ptr = cmd.parent; ptr != nullptr; ptr = ptr->parent) {
    result.insert(result.begin(), ' ');
    result.insert(result.begin(), ptr->name.begin(), ptr->name.end());
  }
  return result;
}

namespace {

using std::accumulate;

// Returns the field size for printing all names in `xs`.
auto field_size(const command::children_list& xs) {
  return accumulate(xs.begin(), xs.end(), size_t{0}, [](size_t x, auto& y) {
    return std::max(x, y->name.size());
  });
}

// Returns the field size for printing all names in `xs`.
auto field_size(const caf::config_option_set& xs) {
  return accumulate(xs.begin(), xs.end(), size_t{0}, [](size_t x, auto& y) {
    // We print parameters in the form "[-h | -? | --help] <type>" (but we omit
    // the type for boolean). So, "[]" adds 2 characters, each short name adds
    // 5 characters with "-X | ", the long name gets the 2 character prefix
    // "--", and finally we add an extra space plus the type name.
    auto tname = y.type_name();
    auto tname_size = tname == "boolean" ? 0u : tname.size() + 3;
    return std::max(x, 4 + (y.short_names().size() * 5) + y.long_name().size()
                         + tname_size);
  });
}

void parameters_helptext(const command& cmd, std::ostream& out) {
  out << "parameters:\n";
  auto fs = field_size(cmd.options);
  for (auto& opt : cmd.options) {
    out << "  ";
    std::string lst = "[";
    for (auto ch : opt.short_names()) {
      lst += '-';
      lst += ch;
      lst += " | ";
    }
    lst += "--";
    lst.insert(lst.end(), opt.long_name().begin(), opt.long_name().end());
    lst += ']';
    auto tname = opt.type_name();
    if (tname != "boolean") {
      lst += " <";
      lst.insert(lst.end(), tname.begin(), tname.end());
      lst += '>';
    }
    out.width(fs);
    out << lst << "  " << opt.description() << '\n';
  }
}

// Prints the helptext for a command without children.
void flat_helptext(const command& cmd, std::ostream& out) {
  // A trivial command without parameters prints its name and description.
  if (cmd.options.empty()) {
    out << "usage: " << full_name(cmd) << "\n\n"
        << cmd.description << "\n\n";
    return;
  }
  // A command with parameters prints 1) its name, 2) a description, and 3) a
  // list of available parameters.
  out << "usage: " << full_name(cmd) << " [<parameters>]\n\n"
      << cmd.description << "\n\n";
  parameters_helptext(cmd, out);
}

void subcommand_helptext(const command& cmd, std::ostream& out) {
  out << "subcommands:\n";
  auto fs = field_size(cmd.children);
  for (auto& child : cmd.children) {
    out << "  ";
    out.width(fs);
    out << child->name << "  " << child->description << '\n';
  }
}

// Prints the helptext for a command without children.
void nested_helptext(const command& cmd, std::ostream& out) {
  // A trivial command without parameters prints name, description and
  // children.
  if (cmd.options.empty()) {
    out << "usage: " << full_name(cmd) << " <command>" << "\n\n"
        << cmd.description  << "\n\n";
    subcommand_helptext(cmd, out);
    return;
  }
  out << "usage: " << full_name(cmd) << " [<parameters>] <command>" << "\n\n"
      << cmd.description << "\n\n";
  parameters_helptext(cmd, out);
  out << '\n';
  subcommand_helptext(cmd, out);
}

} // namespace <anonymous>

void helptext(const command& cmd, std::ostream& out) {
  // Make sure fields are filled left-to-right.
  out << std::left;
  // Dispatch based on whether or nood cmd has children.
  if (cmd.children.empty())
    flat_helptext(cmd, out);
  else
    nested_helptext(cmd, out);
}

std::string helptext(const command& cmd) {
  std::ostringstream oss;
  helptext(cmd, oss);
  return oss.str();
}

/*

std::string command::usage() const {
  std::stringstream result;
  result << opts_.help_text();
  if (!children_.empty()) {
    result << "\nsubcommands:\n";
    for (auto& child : children_)
      result << "  " << child->name() << "\n";
  }
  return result.str();
}

std::string command::full_name() const {
  std::string result;
  if (is_root())
    return result;
  result = parent_->full_name();
  if (!result.empty())
    result += ' ';
  result += name_;
  return result;
}

caf::error command::proceed(caf::actor_system&,
                            const caf::config_value_map& options,
                            argument_iterator begin, argument_iterator end) {
  VAST_UNUSED(options, begin, end);
  VAST_TRACE(VAST_ARG(std::string{name_}), VAST_ARG("args", begin, end),
             VAST_ARG(options));
  return caf::none;
}

caf::message command::run_impl(caf::actor_system&,
                               const caf::config_value_map& options,
                               argument_iterator begin, argument_iterator end) {
  VAST_UNUSED(options, begin, end);
  VAST_TRACE(VAST_ARG(std::string{name_}), VAST_ARG("args", begin, end),
             VAST_ARG(options));
  usage();
  return caf::none;
}

caf::message command::wrap_error(caf::error err) {
  return caf::make_message(std::move(err));
}

caf::error command::parse_error(caf::pec code, argument_iterator error_position,
                                argument_iterator begin,
                                argument_iterator end) const {
  std::stringstream result;
  result << "Failed to parse command" << "\n";
  result << "  Command: " << name() << " " << detail::join(begin, end, " ")
         << "\n";
  result << "  Description: " << to_string(code) << "\n";
  if (error_position != end)
    result << "  Position: " << *error_position << "";
  else
    result << "  Position: at the end";
  return make_error(ec::parse_error, result.str());
}

caf::error command::unknown_subcommand_error(argument_iterator error_position,
                                             argument_iterator end) const {
  VAST_ASSERT(error_position != end);
  auto& str = *error_position;
  std::string helptext;
  if (!str.empty() && str.front() == '-') {
    helptext = "invalid option: ";
    helptext += str;
  } else {
    helptext = "unknown command: ";
    if (!is_root()) {
      helptext += full_name();
      helptext += ' ';
    }
    helptext += str;
  }
  return make_error(ec::syntax_error, std::move(helptext));
}
*/

} // namespace vast
<|endoftext|>


<commit_before>// init: The initial user-level program

#include "types.h"
#include "user.h"
#include <fcntl.h>
#include "lib.h"
#include "major.h"

static const char *sh_argv[] = { "sh", 0 };
static const char *app_argv[][MAXARG] = {
#ifdef LWIP
  { "telnetd", 0 },
  { "httpd", 0 },
#endif
};

static struct {
  const char* name;
  int major;
} dev[] = {
  { "/dev/netif",     MAJ_NETIF },
  { "/dev/sampler",   MAJ_SAMPLER },
  { "/dev/lockstat",  MAJ_LOCKSTAT },
  { "/dev/stat",      MAJ_STAT },
  { "/dev/cmdline",   MAJ_CMDLINE},
  { "/dev/gc",   MAJ_GC},
  { "/dev/kconfig",   MAJ_KCONFIG},
  { "/dev/kstats",    MAJ_KSTATS},
};

static int
startone(const char **argv)
{
  int pid;

  pid = fork(0);
  if(pid < 0){
    fprintf(1, "init: fork failed\n");
    exit();
  }
  if(pid == 0){
    exec(argv[0], argv);
    fprintf(1, "init: exec %s failed\n", argv[0]);
    exit();
  }
  return pid;
}

static void
runcmdline(void)
{
  const char* argv[4] = { "sh", "-c", 0 };
  char buf[256];
  char* b;
  long r;
  int fd;

  fd = open("/dev/cmdline", O_RDONLY);
  if (fd < 0)
    return;

  r = read(fd, buf, sizeof(buf)-1);
  if (r < 0)
    return;
  buf[r] = 0;

  if ((b = strchr(buf, '$'))) {
    argv[2] = b+1;
    startone(argv);
  }
}

int
main(void)
{
  int pid, wpid;

  if(open("console", O_RDWR) < 0){
    mknod("console", 1, 1);
    open("console", O_RDWR);
  }
  dup(0);  // stdout
  dup(0);  // stderr

  mkdir("dev", 0777);
  for (int i = 0; i < NELEM(dev); i++)
    if (mknod(dev[i].name, dev[i].major, 1) < 0)
      fprintf(2, "init: mknod %s failed\n", dev[i].name);

  for (u32 i = 0; i < NELEM(app_argv); i++)
    startone(app_argv[i]);

  runcmdline();

  for(;;){
    pid = startone(sh_argv);
    while((wpid=wait(-1)) >= 0 && wpid != pid)
      fprintf(1, "zombie!\n");
  }
}
<commit_msg>init: Print time at boot<commit_after>// init: The initial user-level program

#include "types.h"
#include "user.h"
#include <fcntl.h>
#include "lib.h"
#include "major.h"
#include <time.h>

static const char *sh_argv[] = { "sh", 0 };
static const char *app_argv[][MAXARG] = {
#ifdef LWIP
  { "telnetd", 0 },
  { "httpd", 0 },
#endif
};

static struct {
  const char* name;
  int major;
} dev[] = {
  { "/dev/netif",     MAJ_NETIF },
  { "/dev/sampler",   MAJ_SAMPLER },
  { "/dev/lockstat",  MAJ_LOCKSTAT },
  { "/dev/stat",      MAJ_STAT },
  { "/dev/cmdline",   MAJ_CMDLINE},
  { "/dev/gc",   MAJ_GC},
  { "/dev/kconfig",   MAJ_KCONFIG},
  { "/dev/kstats",    MAJ_KSTATS},
};

static int
startone(const char **argv)
{
  int pid;

  pid = fork(0);
  if(pid < 0){
    fprintf(1, "init: fork failed\n");
    exit();
  }
  if(pid == 0){
    exec(argv[0], argv);
    fprintf(1, "init: exec %s failed\n", argv[0]);
    exit();
  }
  return pid;
}

static void
runcmdline(void)
{
  const char* argv[4] = { "sh", "-c", 0 };
  char buf[256];
  char* b;
  long r;
  int fd;

  fd = open("/dev/cmdline", O_RDONLY);
  if (fd < 0)
    return;

  r = read(fd, buf, sizeof(buf)-1);
  if (r < 0)
    return;
  buf[r] = 0;

  if ((b = strchr(buf, '$'))) {
    argv[2] = b+1;
    startone(argv);
  }
}

int
main(void)
{
  int pid, wpid;

  if(open("console", O_RDWR) < 0){
    mknod("console", 1, 1);
    open("console", O_RDWR);
  }
  dup(0);  // stdout
  dup(0);  // stderr

  mkdir("dev", 0777);
  for (int i = 0; i < NELEM(dev); i++)
    if (mknod(dev[i].name, dev[i].major, 1) < 0)
      fprintf(2, "init: mknod %s failed\n", dev[i].name);

  for (u32 i = 0; i < NELEM(app_argv); i++)
    startone(app_argv[i]);

  time_t now = time(nullptr);
  printf("init complete at %s", ctime(&now));

  runcmdline();

  for(;;){
    pid = startone(sh_argv);
    while((wpid=wait(-1)) >= 0 && wpid != pid)
      fprintf(1, "zombie!\n");
  }
}
<|endoftext|>