<commit_before>/*
   Copyright (C) 2018 Alexander Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   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.

 */

#include "DataStorage_p.hpp"

#include "ApiUtils.hpp"
#include "TLTypesDebug.hpp"
#include "Utils.hpp"
#include "Debug.hpp"

#include "TelegramNamespace_p.hpp"

#include <QLoggingCategory>

namespace Telegram {

namespace Client {

/*!
    \class Telegram::Client::DataStorage
    \brief The DataStorage class provides a basic interface for session
           data management
    \inmodule TelegramQt
    \ingroup Client

    \sa AccountStorage
*/

DataStorage::DataStorage(QObject *parent) :
    DataStorage(new DataStoragePrivate(), parent)
{
}

DcConfiguration DataStorage::serverConfiguration() const
{
    Q_D(const DataStorage);
    return d->m_serverConfig;
}

void DataStorage::setServerConfiguration(const DcConfiguration &configuration)
{
    Q_D(DataStorage);
    d->m_serverConfig = configuration;
}

QVector<Peer> DataStorage::dialogs() const
{
    Q_D(const DataStorage);
    const TLMessagesDialogs dialogs = d->m_api->m_dialogs;
    QVector<Peer> result;
    result.reserve(dialogs.count);
    for (const TLDialog &dialog : dialogs.dialogs) {
        result.append(Utils::toPublicPeer(dialog.peer));
    }
    return result;
}

QVector<Peer> DataStorage::contactList() const
{
    Q_D(const DataStorage);
    const TLVector<TLContact> contacts = d->m_api->m_contactList;
    QVector<Peer> result;
    result.reserve(contacts.count());
    for (const TLContact &contact : contacts) {
        result.append(Peer::fromUserId(contact.userId));
    }
    return result;
}

quint32 DataStorage::selfUserId() const
{
    Q_D(const DataStorage);
    return d->m_api->selfUserId();
}

bool DataStorage::getDialogInfo(DialogInfo *info, const Peer &peer) const
{
    Q_D(const DataStorage);
    const auto &dialogs = d->m_api->m_dialogs;
    for (const TLDialog &dialog : dialogs.dialogs) {
        Telegram::Peer thisDialogPeer = Utils::toPublicPeer(dialog.peer);
        if (thisDialogPeer == peer) {
            TLDialog *infoData = Telegram::DialogInfo::Private::get(info);
            *infoData = dialog;
            return true;
        }
    }
    qDebug() << Q_FUNC_INFO << "Unknown dialog" << peer.toString();
    return false;
}

bool DataStorage::getUserInfo(UserInfo *info, quint32 userId) const
{
    Q_D(const DataStorage);
    const auto &users = d->m_api->m_users;
    if (!users.contains(userId)) {
        qDebug() << Q_FUNC_INFO << "Unknown user" << userId;
        return false;
    }

    const TLUser *user = users.value(userId);
    TLUser *infoData = Telegram::UserInfo::Private::get(info);
    *infoData = *user;
    return true;
}

bool DataStorage::getChatInfo(ChatInfo *info, const Telegram::Peer &peer) const
{
    Q_D(const DataStorage);
    const quint32 chatId = peer.id;
    const auto &chats = d->m_api->m_chats;
    if (!chats.contains(chatId)) {
        qDebug() << Q_FUNC_INFO << "Unknown user" << chatId;
        return false;
    }

    const TLChat *chat = chats.value(chatId);
    TLChat *infoData = Telegram::ChatInfo::Private::get(info);
    *infoData = *chat;
    return true;
}

bool DataStorage::getMessage(Message *message, const Peer &peer, quint32 messageId)
{
    Q_D(const DataStorage);
    const TLMessage *m = d->m_api->getMessage(peer, messageId);
    if (!m) {
        qDebug() << Q_FUNC_INFO << "Unknown message" << peer << messageId;
        return false;
    }
    const TLMessageMedia &media = m->media;

    message->id = messageId;
    message->type = Telegram::Utils::getPublicMessageType(media);
    message->fromId = m->fromId;
    message->timestamp = m->date;
    message->text = m->message;
    message->flags = TelegramNamespace::MessageFlagNone;
    if (m->out()) {
        message->flags |= TelegramNamespace::MessageFlagOut;
    }
    if (m->flags & TLMessage::FwdFrom) {
        message->flags |= TelegramNamespace::MessageFlagForwarded;
        if (m->fwdFrom.flags & TLMessageFwdHeader::FromId) {
            //message->setForwardFromPeer((m->fwdFrom))
        }
    }
    return true;
}

bool DataStorage::getMessageMediaInfo(MessageMediaInfo *info, const Peer &peer, quint32 messageId)
{
    Q_D(const DataStorage);
    const TLMessage *m = nullptr;
    if (peer.type == Peer::Channel) {
        quint64 key = DataInternalApi::channelMessageToKey(peer.id, messageId);
        m = d->m_api->m_channelMessages.value(key);
    } else {
        m = d->m_api->m_clientMessages.value(messageId);
    }
    if (!m) {
        qDebug() << Q_FUNC_INFO << "Unknown message" << peer << messageId;
        return false;
    }
    const TLMessageMedia &media = m->media;

    MessageMediaInfo::Private *privateInfo = MessageMediaInfo::Private::get(info);
    *privateInfo = media;
    return true;
}

DataStorage::DataStorage(DataStoragePrivate *priv, QObject *parent)
    : QObject(parent),
      d(priv)
{
    d->m_api = new DataInternalApi(this);
}

InMemoryDataStorage::InMemoryDataStorage(QObject *parent) :
    DataStorage(parent)
{
}

DataInternalApi::DataInternalApi(QObject *parent) :
    QObject(parent)
{
}

DataInternalApi::~DataInternalApi()
{
}

const TLUser *DataInternalApi::getSelfUser() const
{
    if (!m_selfUserId) {
        return nullptr;
    }
    return m_users.value(m_selfUserId);
}

const TLMessage *DataInternalApi::getMessage(const Peer &peer, quint32 messageId) const
{
    if (peer.type == Peer::Channel) {
        quint64 key = DataInternalApi::channelMessageToKey(peer.id, messageId);
        return m_channelMessages.value(key);
    }
    return m_clientMessages.value(messageId);
}

void DataInternalApi::processData(const TLMessage &message)
{
    TLMessage *m = nullptr;
    if (message.toId.tlType == TLValue::PeerChannel) {
        const quint64 key = channelMessageToKey(message.toId.channelId, message.id);
        if (!m_channelMessages.contains(key)) {
            m_channelMessages.insert(key, new TLMessage());
        }
        m = m_channelMessages.value(key);
    } else {
        const quint32 key = message.id;
        if (!m_clientMessages.contains(key)) {
            m_clientMessages.insert(key, new TLMessage());
        }
        m = m_clientMessages.value(key);
    }
    *m = message;
}

void DataInternalApi::processData(const TLVector<TLChat> &chats)
{
    for (const TLChat &chat : chats) {
        processData(chat);
    }
}

void DataInternalApi::processData(const TLChat &chat)
{
    if (!m_chats.contains(chat.id)) {
        TLChat *newChatInstance = new TLChat(chat);
        m_chats.insert(chat.id, newChatInstance);
    } else {
        *m_chats[chat.id] = chat;
    }
}

void DataInternalApi::processData(const TLVector<TLUser> &users)
{
    for (const TLUser &user : users) {
        processData(user);
    }
}

void DataInternalApi::processData(const TLUser &user)
{
    TLUser *existsUser = m_users.value(user.id);
    if (existsUser) {
        *existsUser = user;
    } else {
        m_users.insert(user.id, new TLUser(user));
    }
    if (user.self()) {
        if (m_selfUserId && (m_selfUserId != user.id)) {
            qWarning() << "Got self user with different id.";
        }
        m_selfUserId = user.id;
    }
}

void DataInternalApi::processData(const TLAuthAuthorization &authorization)
{
    processData(authorization.user);
}

void DataInternalApi::processData(const TLMessagesDialogs &dialogs)
{
    //qDebug() << Q_FUNC_INFO << dialogs;
    m_dialogs = dialogs;
    processData(dialogs.users);
    processData(dialogs.chats);
    for (const TLMessage &message : dialogs.messages) {
        processData(message);
    }
}

void DataInternalApi::processData(const TLMessagesMessages &messages)
{
    processData(messages.users);
    processData(messages.chats);
    for (const TLMessage &message : messages.messages) {
        processData(message);
    }
}

void DataInternalApi::setContactList(const TLVector<TLContact> &contacts)
{
    m_contactList = contacts;
}

quint64 DataInternalApi::enqueueMessage(const Telegram::Peer peer, const QString &message, quint32 replyToMsgId)
{
    SentMessage sentMessage;
    sentMessage.peer = peer;
    sentMessage.text = message;
    sentMessage.replyToMsgId = replyToMsgId;
    sentMessage.randomId = Utils::randomBytes<quint64>();
    m_queuedMessages.append(sentMessage);
    return sentMessage.randomId;
}

DataInternalApi::SentMessage DataInternalApi::getQueuedMessage(quint64 randomMessageId) const
{
    for (const SentMessage &message : m_queuedMessages) {
        if (message.randomId == randomMessageId) {
            return message;
        }
    }
    return SentMessage();
}

DataInternalApi::SentMessage DataInternalApi::dequeueMessage(quint64 messageRandomId, quint32 messageId)
{
    if (m_queuedMessages.isEmpty()) {
        qWarning() << Q_FUNC_INFO << "Invalid dequeue request (message queue is empty):" << messageRandomId << messageId;
        return SentMessage();
    }
    if (m_queuedMessages.head().randomId == messageRandomId) {
        return m_queuedMessages.dequeue();
    }
    for (int i = 0; i < m_queuedMessages.count(); ++i) {
        if (m_queuedMessages.at(i).randomId == messageRandomId) {
            return m_queuedMessages.takeAt(i);
        }
    }
    qWarning() << Q_FUNC_INFO << "Invalid dequeue request (message not found):" << messageRandomId << messageId;
    return SentMessage();
}

TLInputPeer DataInternalApi::toInputPeer(const Peer &peer) const
{
    TLInputPeer inputPeer;
    switch (peer.type) {
    case Telegram::Peer::Chat:
        inputPeer.tlType = TLValue::InputPeerChat;
        inputPeer.chatId = peer.id;
        break;
    case Telegram::Peer::Channel:
        if (m_chats.contains(peer.id)) {
            inputPeer.tlType = TLValue::InputPeerChannel;
            inputPeer.channelId = peer.id;
            inputPeer.accessHash = m_chats.value(peer.id)->accessHash;
        } else {
            qWarning() << Q_FUNC_INFO << "Unknown public channel id" << peer.id;
        }
        break;
    case Telegram::Peer::User:
        if (peer.id == m_selfUserId) {
            inputPeer.tlType = TLValue::InputPeerSelf;
        } else {
            if (m_users.contains(peer.id)) {
                inputPeer.tlType = TLValue::InputPeerUser;
                inputPeer.userId = peer.id;
                inputPeer.accessHash = m_users.value(peer.id)->accessHash;
            } else {
                qWarning() << Q_FUNC_INFO << "Unknown user" << peer.id;
            }
        }
        break;
    default:
        qWarning() << Q_FUNC_INFO << "Unknown peer type" << peer.type << "(id:" << peer.id << ")";
        break;
    }
    return inputPeer;
}

TLInputUser DataInternalApi::toInputUser(quint32 userId) const
{
    TLInputUser inputUser;
    if (userId == selfUserId()) {
        inputUser.tlType = TLValue::InputUserSelf;
        return inputUser;
    }
    const TLUser *user = m_users.value(userId);
    if (user) {
        if (user->tlType == TLValue::User) {
            inputUser.tlType = TLValue::InputUser;
            inputUser.userId = user->id;
            inputUser.accessHash = user->accessHash;
        } else {
            qWarning() << Q_FUNC_INFO << "Unknown user type: " << QString::number(user->tlType, 16);
        }
    } else {
        qWarning() << Q_FUNC_INFO << "Unknown user.";
    }
    return inputUser;
}

quint64 DataInternalApi::channelMessageToKey(quint32 channelId, quint32 messageId)
{
    quint64 key = channelId;
    return (key << 32) + messageId;
}

DataStoragePrivate *DataStoragePrivate::get(DataStorage *parent)
{
    return parent->d;
}

} // Client namespace

} // Telegram namespace
<commit_msg>DataStorage: Use extracted getMessage()<commit_after>/*
   Copyright (C) 2018 Alexander Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   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.

 */

#include "DataStorage_p.hpp"

#include "ApiUtils.hpp"
#include "TLTypesDebug.hpp"
#include "Utils.hpp"
#include "Debug.hpp"

#include "TelegramNamespace_p.hpp"

#include <QLoggingCategory>

namespace Telegram {

namespace Client {

/*!
    \class Telegram::Client::DataStorage
    \brief The DataStorage class provides a basic interface for session
           data management
    \inmodule TelegramQt
    \ingroup Client

    \sa AccountStorage
*/

DataStorage::DataStorage(QObject *parent) :
    DataStorage(new DataStoragePrivate(), parent)
{
}

DcConfiguration DataStorage::serverConfiguration() const
{
    Q_D(const DataStorage);
    return d->m_serverConfig;
}

void DataStorage::setServerConfiguration(const DcConfiguration &configuration)
{
    Q_D(DataStorage);
    d->m_serverConfig = configuration;
}

QVector<Peer> DataStorage::dialogs() const
{
    Q_D(const DataStorage);
    const TLMessagesDialogs dialogs = d->m_api->m_dialogs;
    QVector<Peer> result;
    result.reserve(dialogs.count);
    for (const TLDialog &dialog : dialogs.dialogs) {
        result.append(Utils::toPublicPeer(dialog.peer));
    }
    return result;
}

QVector<Peer> DataStorage::contactList() const
{
    Q_D(const DataStorage);
    const TLVector<TLContact> contacts = d->m_api->m_contactList;
    QVector<Peer> result;
    result.reserve(contacts.count());
    for (const TLContact &contact : contacts) {
        result.append(Peer::fromUserId(contact.userId));
    }
    return result;
}

quint32 DataStorage::selfUserId() const
{
    Q_D(const DataStorage);
    return d->m_api->selfUserId();
}

bool DataStorage::getDialogInfo(DialogInfo *info, const Peer &peer) const
{
    Q_D(const DataStorage);
    const auto &dialogs = d->m_api->m_dialogs;
    for (const TLDialog &dialog : dialogs.dialogs) {
        Telegram::Peer thisDialogPeer = Utils::toPublicPeer(dialog.peer);
        if (thisDialogPeer == peer) {
            TLDialog *infoData = Telegram::DialogInfo::Private::get(info);
            *infoData = dialog;
            return true;
        }
    }
    qDebug() << Q_FUNC_INFO << "Unknown dialog" << peer.toString();
    return false;
}

bool DataStorage::getUserInfo(UserInfo *info, quint32 userId) const
{
    Q_D(const DataStorage);
    const auto &users = d->m_api->m_users;
    if (!users.contains(userId)) {
        qDebug() << Q_FUNC_INFO << "Unknown user" << userId;
        return false;
    }

    const TLUser *user = users.value(userId);
    TLUser *infoData = Telegram::UserInfo::Private::get(info);
    *infoData = *user;
    return true;
}

bool DataStorage::getChatInfo(ChatInfo *info, const Telegram::Peer &peer) const
{
    Q_D(const DataStorage);
    const quint32 chatId = peer.id;
    const auto &chats = d->m_api->m_chats;
    if (!chats.contains(chatId)) {
        qDebug() << Q_FUNC_INFO << "Unknown user" << chatId;
        return false;
    }

    const TLChat *chat = chats.value(chatId);
    TLChat *infoData = Telegram::ChatInfo::Private::get(info);
    *infoData = *chat;
    return true;
}

bool DataStorage::getMessage(Message *message, const Peer &peer, quint32 messageId)
{
    Q_D(const DataStorage);
    const TLMessage *m = d->m_api->getMessage(peer, messageId);
    if (!m) {
        qDebug() << Q_FUNC_INFO << "Unknown message" << peer << messageId;
        return false;
    }
    const TLMessageMedia &media = m->media;

    message->id = messageId;
    message->type = Telegram::Utils::getPublicMessageType(media);
    message->fromId = m->fromId;
    message->timestamp = m->date;
    message->text = m->message;
    message->flags = TelegramNamespace::MessageFlagNone;
    if (m->out()) {
        message->flags |= TelegramNamespace::MessageFlagOut;
    }
    if (m->flags & TLMessage::FwdFrom) {
        message->flags |= TelegramNamespace::MessageFlagForwarded;
        if (m->fwdFrom.flags & TLMessageFwdHeader::FromId) {
            //message->setForwardFromPeer((m->fwdFrom))
        }
    }
    return true;
}

bool DataStorage::getMessageMediaInfo(MessageMediaInfo *info, const Peer &peer, quint32 messageId)
{
    Q_D(const DataStorage);
    const TLMessage *m = d->m_api->getMessage(peer, messageId);
    if (!m) {
        qDebug() << Q_FUNC_INFO << "Unknown message" << peer << messageId;
        return false;
    }
    const TLMessageMedia &media = m->media;

    MessageMediaInfo::Private *privateInfo = MessageMediaInfo::Private::get(info);
    *privateInfo = media;
    return true;
}

DataStorage::DataStorage(DataStoragePrivate *priv, QObject *parent)
    : QObject(parent),
      d(priv)
{
    d->m_api = new DataInternalApi(this);
}

InMemoryDataStorage::InMemoryDataStorage(QObject *parent) :
    DataStorage(parent)
{
}

DataInternalApi::DataInternalApi(QObject *parent) :
    QObject(parent)
{
}

DataInternalApi::~DataInternalApi()
{
}

const TLUser *DataInternalApi::getSelfUser() const
{
    if (!m_selfUserId) {
        return nullptr;
    }
    return m_users.value(m_selfUserId);
}

const TLMessage *DataInternalApi::getMessage(const Peer &peer, quint32 messageId) const
{
    if (peer.type == Peer::Channel) {
        quint64 key = DataInternalApi::channelMessageToKey(peer.id, messageId);
        return m_channelMessages.value(key);
    }
    return m_clientMessages.value(messageId);
}

void DataInternalApi::processData(const TLMessage &message)
{
    TLMessage *m = nullptr;
    if (message.toId.tlType == TLValue::PeerChannel) {
        const quint64 key = channelMessageToKey(message.toId.channelId, message.id);
        if (!m_channelMessages.contains(key)) {
            m_channelMessages.insert(key, new TLMessage());
        }
        m = m_channelMessages.value(key);
    } else {
        const quint32 key = message.id;
        if (!m_clientMessages.contains(key)) {
            m_clientMessages.insert(key, new TLMessage());
        }
        m = m_clientMessages.value(key);
    }
    *m = message;
}

void DataInternalApi::processData(const TLVector<TLChat> &chats)
{
    for (const TLChat &chat : chats) {
        processData(chat);
    }
}

void DataInternalApi::processData(const TLChat &chat)
{
    if (!m_chats.contains(chat.id)) {
        TLChat *newChatInstance = new TLChat(chat);
        m_chats.insert(chat.id, newChatInstance);
    } else {
        *m_chats[chat.id] = chat;
    }
}

void DataInternalApi::processData(const TLVector<TLUser> &users)
{
    for (const TLUser &user : users) {
        processData(user);
    }
}

void DataInternalApi::processData(const TLUser &user)
{
    TLUser *existsUser = m_users.value(user.id);
    if (existsUser) {
        *existsUser = user;
    } else {
        m_users.insert(user.id, new TLUser(user));
    }
    if (user.self()) {
        if (m_selfUserId && (m_selfUserId != user.id)) {
            qWarning() << "Got self user with different id.";
        }
        m_selfUserId = user.id;
    }
}

void DataInternalApi::processData(const TLAuthAuthorization &authorization)
{
    processData(authorization.user);
}

void DataInternalApi::processData(const TLMessagesDialogs &dialogs)
{
    //qDebug() << Q_FUNC_INFO << dialogs;
    m_dialogs = dialogs;
    processData(dialogs.users);
    processData(dialogs.chats);
    for (const TLMessage &message : dialogs.messages) {
        processData(message);
    }
}

void DataInternalApi::processData(const TLMessagesMessages &messages)
{
    processData(messages.users);
    processData(messages.chats);
    for (const TLMessage &message : messages.messages) {
        processData(message);
    }
}

void DataInternalApi::setContactList(const TLVector<TLContact> &contacts)
{
    m_contactList = contacts;
}

quint64 DataInternalApi::enqueueMessage(const Telegram::Peer peer, const QString &message, quint32 replyToMsgId)
{
    SentMessage sentMessage;
    sentMessage.peer = peer;
    sentMessage.text = message;
    sentMessage.replyToMsgId = replyToMsgId;
    sentMessage.randomId = Utils::randomBytes<quint64>();
    m_queuedMessages.append(sentMessage);
    return sentMessage.randomId;
}

DataInternalApi::SentMessage DataInternalApi::getQueuedMessage(quint64 randomMessageId) const
{
    for (const SentMessage &message : m_queuedMessages) {
        if (message.randomId == randomMessageId) {
            return message;
        }
    }
    return SentMessage();
}

DataInternalApi::SentMessage DataInternalApi::dequeueMessage(quint64 messageRandomId, quint32 messageId)
{
    if (m_queuedMessages.isEmpty()) {
        qWarning() << Q_FUNC_INFO << "Invalid dequeue request (message queue is empty):" << messageRandomId << messageId;
        return SentMessage();
    }
    if (m_queuedMessages.head().randomId == messageRandomId) {
        return m_queuedMessages.dequeue();
    }
    for (int i = 0; i < m_queuedMessages.count(); ++i) {
        if (m_queuedMessages.at(i).randomId == messageRandomId) {
            return m_queuedMessages.takeAt(i);
        }
    }
    qWarning() << Q_FUNC_INFO << "Invalid dequeue request (message not found):" << messageRandomId << messageId;
    return SentMessage();
}

TLInputPeer DataInternalApi::toInputPeer(const Peer &peer) const
{
    TLInputPeer inputPeer;
    switch (peer.type) {
    case Telegram::Peer::Chat:
        inputPeer.tlType = TLValue::InputPeerChat;
        inputPeer.chatId = peer.id;
        break;
    case Telegram::Peer::Channel:
        if (m_chats.contains(peer.id)) {
            inputPeer.tlType = TLValue::InputPeerChannel;
            inputPeer.channelId = peer.id;
            inputPeer.accessHash = m_chats.value(peer.id)->accessHash;
        } else {
            qWarning() << Q_FUNC_INFO << "Unknown public channel id" << peer.id;
        }
        break;
    case Telegram::Peer::User:
        if (peer.id == m_selfUserId) {
            inputPeer.tlType = TLValue::InputPeerSelf;
        } else {
            if (m_users.contains(peer.id)) {
                inputPeer.tlType = TLValue::InputPeerUser;
                inputPeer.userId = peer.id;
                inputPeer.accessHash = m_users.value(peer.id)->accessHash;
            } else {
                qWarning() << Q_FUNC_INFO << "Unknown user" << peer.id;
            }
        }
        break;
    default:
        qWarning() << Q_FUNC_INFO << "Unknown peer type" << peer.type << "(id:" << peer.id << ")";
        break;
    }
    return inputPeer;
}

TLInputUser DataInternalApi::toInputUser(quint32 userId) const
{
    TLInputUser inputUser;
    if (userId == selfUserId()) {
        inputUser.tlType = TLValue::InputUserSelf;
        return inputUser;
    }
    const TLUser *user = m_users.value(userId);
    if (user) {
        if (user->tlType == TLValue::User) {
            inputUser.tlType = TLValue::InputUser;
            inputUser.userId = user->id;
            inputUser.accessHash = user->accessHash;
        } else {
            qWarning() << Q_FUNC_INFO << "Unknown user type: " << QString::number(user->tlType, 16);
        }
    } else {
        qWarning() << Q_FUNC_INFO << "Unknown user.";
    }
    return inputUser;
}

quint64 DataInternalApi::channelMessageToKey(quint32 channelId, quint32 messageId)
{
    quint64 key = channelId;
    return (key << 32) + messageId;
}

DataStoragePrivate *DataStoragePrivate::get(DataStorage *parent)
{
    return parent->d;
}

} // Client namespace

} // Telegram namespace
<|endoftext|>


<commit_before>#include "Histo.h"
#include <vector>
#include <iostream>

using namespace std;

Histo::Histo() {output_reset();}

Histo::Histo(int in, double imin, double imax) {
	set(in,imin,imax);
	output_reset();
}

void Histo::set(int in, double imin, double imax) {
	n = in;
	min = imin;
	max = imax;

	width = (max-min)/n;
	min_border = min + width;
	max_border = max - width;
	min_output = min + width * .5;

	hist.clear();
	hist.resize(n);
}

void Histo::set(int in, double imax) {set(in,-imax,imax);}

void Histo::add(double val) {
	count++;
	if ( val >= max || val < min ) return;
	if ( val < min_border ) hist[0]++;
	else if (val >= max_border ) hist[n-1]++;
	else hist[ (int) ( (val-min) / width ) ]++;
}

bool Histo::output(ostream& os) {
	if ( n_out >= n ) return false;
	os.precision(8);
	os << scientific;
	os << min_output + n_out * width << " ";
	os << hist[n_out] << " ";
	n_out++;
	return true;
}

bool Histo::output() {return output(cout);}

void Histo::output_reset() {n_out=0;}

void Histo::norm() {
	double scale{count * width};
	for (auto& s : hist) s /= scale;
}
<commit_msg>count initiert Ausgabe auf Tabulatoren umgestellt<commit_after>#include "Histo.h"
#include <vector>
#include <iostream>

using namespace std;

Histo::Histo() {output_reset();}

Histo::Histo(int in, double imin, double imax) {
	set(in,imin,imax);
	output_reset();
}

void Histo::set(int in, double imin, double imax) {
	n = in;
	min = imin;
	max = imax;

	width = (max-min)/n;
	min_border = min + width;
	max_border = max - width;
	min_output = min + width * .5;

	hist.clear();
	hist.resize(n);
	count = 0;
}

void Histo::set(int in, double imax) {set(in,-imax,imax);}

void Histo::add(double val) {
	count++;
	if ( val >= max || val < min ) return;
	if ( val < min_border ) hist[0]++;
	else if (val >= max_border ) hist[n-1]++;
	else hist[ (int) ( (val-min) / width ) ]++;
}

bool Histo::output(ostream& os) {
	if ( n_out >= n ) {
		os << "        \t        \t";
		return false;
	}
	os.precision(8);
	os << scientific;
	os << min_output + n_out * width << '\t';
	os << hist[n_out] << '\t';
	n_out++;
	return true;
}

bool Histo::output() {return output(cout);}

void Histo::output_reset() {n_out=0;}

void Histo::norm() {
	double scale{count * width};
	for (auto& s : hist) s /= scale;
}
<|endoftext|>


<commit_before>#include "tables.h"

/*This information was previously in main but was moved out to make test programs
 */

///////////////INITIALIZIING////////////////
////////////////////////////////////////////
namespace editor
{
    int WIN_HEIGHT, WIN_WIDTH;
    WIN *ptrnwin;
    WIN *instwin;
    WIN *metawin;
    WIN *volwin;
    WIN *wavewin;
    WIN *pulsewin;
    WIN *dialog;

    WIN *wingroup;
    WIN *inputwin;
    WIN *lastwin;

    bool running;

    int numBuffer;
    char charBuffer[CHARBUFFER_SIZE];
    char charInputBuffer[CMDBAR_SIZE];

    char charInputHistory[HISTORY_SIZE][CMDBAR_SIZE];
    char history_head;
    char history_size;

    char lastSongPath[LASTSONG_SIZE];
    char textCursorPos;
    bool *muted_tracks;
    float playamp;

    Song *song;
    int daemonpid;
    Song *playback;
    unsigned char playback_length;
    unsigned char playback_mark;
    Instrument *selinst;
}

//Pattern Editor
namespace patternedtr
{
    std::map<int,unsigned int> notemap;
    Pattern *selptrn;
    unsigned char viewporttrack;
    unsigned char viewportrow;

    unsigned char maxtracksviewport;
    unsigned char maxrowsviewport;

    unsigned char selrow;
    unsigned char seltrack;
    unsigned char seltrackseg;
    unsigned char selorder;

    unsigned char selinstrument;
    unsigned char edit_step;
    unsigned char row_underline;
    unsigned char octave;
    unsigned char key;
    unsigned char scalespinner;
    unsigned char scaleconst[12];//Scale construction pattern
    unsigned int entryclipboard;

    //METADATA (Main controls)
    unsigned char metaobjindex;
    bool metaobjedit;

    unsigned char selobjmeta;//x
    unsigned char selrowmeta;//y

}

//Instrument Editor
namespace instedtr
{
    unsigned short waveclipboard;
    unsigned short pulseclipboard;
    unsigned short volclipboard;

    bool instobjedit;
    unsigned char selinstrow;
    unsigned char selinstobj;
    unsigned short selwavrow;
    unsigned short selpulrow;
    unsigned char selpulseg;

    unsigned char selwavseg;
    unsigned short viewportwave;
    unsigned short viewportpulse;
    unsigned char viewportvol;

    unsigned char selvolrow;
    unsigned char selvolseg;
}
/////////////DONE INITIALIZING/////////////
///////////////////////////////////////////
<commit_msg>Moved functions from utility into externinit.cpp<commit_after>#include "tables.h"
#include "daemoncomm.h"

/*This information was previously in main but was moved out to make test programs
 */

///////////////INITIALIZIING////////////////
////////////////////////////////////////////
namespace editor
{
    int WIN_HEIGHT, WIN_WIDTH;
    WIN *ptrnwin;
    WIN *instwin;
    WIN *metawin;
    WIN *volwin;
    WIN *wavewin;
    WIN *pulsewin;
    WIN *dialog;

    WIN *wingroup;
    WIN *inputwin;
    WIN *lastwin;

    bool running;

    int numBuffer;
    char charBuffer[CHARBUFFER_SIZE];
    char charInputBuffer[CMDBAR_SIZE];

    char charInputHistory[HISTORY_SIZE][CMDBAR_SIZE];
    char history_head;
    char history_size;

    char lastSongPath[LASTSONG_SIZE];
    char textCursorPos;
    bool *muted_tracks;
    float playamp;

    Song *song;
    int daemonpid;
    Song *playback;
    unsigned char playback_length;
    unsigned char playback_mark;
    Instrument *selinst;
}

//Pattern Editor
namespace patternedtr
{
    std::map<int,unsigned int> notemap;
    Pattern *selptrn;
    unsigned char viewporttrack;
    unsigned char viewportrow;

    unsigned char maxtracksviewport;
    unsigned char maxrowsviewport;

    unsigned char selrow;
    unsigned char seltrack;
    unsigned char seltrackseg;
    unsigned char selorder;

    unsigned char selinstrument;
    unsigned char edit_step;
    unsigned char row_underline;
    unsigned char octave;
    unsigned char key;
    unsigned char scalespinner;
    unsigned char scaleconst[CHROMATIC_NOTES];//Scale construction pattern
    unsigned int entryclipboard;

    //METADATA (Main controls)
    unsigned char metaobjindex;
    bool metaobjedit;

    unsigned char selobjmeta;//x
    unsigned char selrowmeta;//y

}

//Instrument Editor
namespace instedtr
{
    unsigned short waveclipboard;
    unsigned short pulseclipboard;
    unsigned short volclipboard;

    bool instobjedit;
    unsigned char selinstrow;
    unsigned char selinstobj;
    unsigned short selwavrow;
    unsigned short selpulrow;
    unsigned char selpulseg;

    unsigned char selwavseg;
    unsigned short viewportwave;
    unsigned short viewportpulse;
    unsigned char viewportvol;

    unsigned char selvolrow;
    unsigned char selvolseg;
}
/////////////DONE INITIALIZING/////////////
///////////////////////////////////////////

bool editor::validateHexChar(char a)
{
    if(a >= 'a')
        a -= 32;
    return(a <= '9' && a >= '0' || a >= 'A' && a <= 'F');
}

bool editor::validate64(char str[2])
{
    //values < 64 have to start with 0 to 3
    //invalid ascii characters are filterred
    return str[0] < '4' && str[0] >= '0' && validateHexChar(str[1]);
}
unsigned char editor::charHex(char c)
{
    if(c<='9' && c>='0')
        return c-'0';
    if(c >= 'a')
        c -= 32;
    if(c>='A' && c <= 'F')
        return c-55;
    return -1;
}

char *editor::byteString(char *string, unsigned char byte)
{
    unsigned char d = byte % 0x10;
    string[1] = hexnums[d];
    byte /=0x10;
    d = byte % 0x10;
    string[0] = hexnums[d];
    return string;
}
char *editor::intString(char *string, unsigned int integer, const unsigned int &strlen)
{
    unsigned char d;
    for(int i = 0; i < strlen; i++)
    {
        d = integer & 0xF;
        string[strlen-i-1]= hexnums[d];
        integer >>= 4;
    }
    return string;
}

char *editor::shortString(char *string,  unsigned short shrt, const unsigned int &strlen)
{
    unsigned char d;
    for(int i = 0; i < strlen; i++)
    {
        d = shrt & 0xF;
        string[strlen-i-1]= hexnums[d];
        shrt >>= 4;
    }
    return string;
}

unsigned char editor::parseHexChar(char str[2])
{
    //values < 64 have to start with 0 to 3
    //invalid ascii characters are filterred
    unsigned char out = charHex(str[0])*0x10;
    out + charHex(str[1]);
    return out;
}

void editor::inform(const char *message)
{
    int length = strlen(message);
    int msglen = length;
    if(length < 64)
        length = 64;
    attroff(-1);
    attron(COLOR_PAIR(patternedtr::COL_META_SSU));
    mvprintw(3,4,"+--------------------------------------------------------------+",stdscr);

    mvprintw(8,4,"+--------------------------------------------------------------+",stdscr);
    attroff(-1);
    attron(COLOR_PAIR(patternedtr::COL_META_US));
    mvprintw(4,4,"                                                                ",stdscr);
    mvprintw(5,4,message,stdscr);
    if(msglen < 64)
        for(int i = msglen; i < 64; i++)
            mvprintw(5, 4+i, " ", stdscr);
    mvprintw(6,4,"                                                                ",stdscr);
    mvprintw(7,4," Press [Space] to continue                                      ",stdscr);
    editor::lastwin = editor::dialog;
    int ch;
    while((ch=getch()) != ' ' && ch != '\n');
    return;
}
bool editor::confirm(const char *message)
{
    int length = strlen(message);
    int msglen = length;
    if(length < 64)
        length = 64;
    attroff(-1);
    attron(COLOR_PAIR(patternedtr::COL_META_SSU));
    //modify these to output to the length
    mvprintw(3,4,"+--------------------------------------------------------------+",stdscr);
    mvprintw(8,4,"+--------------------------------------------------------------+",stdscr);
    attroff(-1);
    attron(COLOR_PAIR(patternedtr::COL_META_US));
    mvprintw(4,4,"                                                                ",stdscr);
    mvprintw(5,4,message,stdscr);
    if(msglen < 64)
        for(int i = msglen; i < 64; i++)
            mvprintw(5, 4+i, " ", stdscr);
    mvprintw(6,4,"                                                                ",stdscr);
    mvprintw(7,4," Are you sure? [y/n]                                            ",stdscr);
    editor::lastwin = editor::dialog;

    int ch;
    while((ch=getch()) != 'y' && ch != 'n' && ch != 27);
    if(ch == 'y')
        return true;
    return false;
}
void editor::displayAbout()
{
    attroff(-1);
    attron(COLOR_PAIR(patternedtr::COL_META_SSU));
    mvprintw(3,4, "+---------------------------------+",stdscr);
    mvprintw(15,4,"+---------------------------------+",stdscr);
    attroff(-1);
    attron(COLOR_PAIR(patternedtr::COL_META_US));
    mvprintw(4,4, "  Welcome to PLEBTracker!       v1 ",stdscr);
    mvprintw(5,4, "                                   ",stdscr);
    mvprintw(6,4, "Press TAB to switch windows        ",stdscr);
    mvprintw(7,4, "Press :   to open the command bar  ",stdscr);
    mvprintw(8,4, "                                   ",stdscr);
    mvprintw(9,4, "To view a list of commands, see    ",stdscr);
    mvprintw(10,4,"   the man page for plebtrkraw(1)  ",stdscr);
    mvprintw(11,4,"                                   ",stdscr);
    mvprintw(12,4,"                      -Dan Frazier ",stdscr);
    mvprintw(13,4,"                                   ",stdscr);
    mvprintw(14,4," Press [Space] to continue         ",stdscr);
    editor::lastwin = editor::dialog;
    int ch;
    while((ch=getch()) != ' ' && ch != '\n');
    return;
}

void editor::setPID(int PID)
{
    //Prepare the paths that the daemon will use
    std::string playpath("/var/tmp/plebtrk/");
    if(PID)
        playpath += std::to_string(PID);
    std::string signalpath(playpath + "signal");
    playpath+= "playback.plb";

    editor::playbackpath = new char[playpath.length()];
    for(int i = 0; i < playpath.length(); i++)
        editor::playbackpath[i]=playpath.at(i);

    editor::signalpath = new char[signalpath.length()];
    for(int i = 0; i < signalpath.length(); i++)
        editor::signalpath[i]=signalpath.at(i);
}
<|endoftext|>


<commit_before>#include <common/buffer.h>
#include <common/test.h>

#define	FILL_BYTE	(0xa5)
#define	FILL_NUMBER	(1024)
#define	FILL_LENGTH	(242)

int
main(void)
{
	TestGroup g("/test/buffer/cut1", "Buffer::cut #1");

	unsigned n;
	for (n = 1; n < 12; n++) {
		Buffer big;

		big.append("Hello, ");
		size_t headerlen = big.length();

		uint8_t fill[n * FILL_LENGTH];
		unsigned i;
		for (i = 0; i < sizeof fill; i++) {
			fill[i] = FILL_BYTE;
		}

		for (i = 0; i < FILL_NUMBER; i++) {
			big.append(fill, sizeof fill);
		}

		big.append("world!\n");

		{
			Test _(g, "Cut fill bytes.");

			Buffer small(big);
			small.cut(headerlen, FILL_NUMBER * sizeof fill);
			if (small.equal("Hello, world!\n"))
				_.pass();
		}

		{
			Test _(g, "Proxy skip and trim.");

			big.cut(0, headerlen);
			big.cut(FILL_NUMBER * sizeof fill,
				big.length() - (FILL_NUMBER * sizeof fill));
			if (big.length() == FILL_NUMBER * sizeof fill)
				_.pass();
		}

		{
			Test _(g, "Correct fill byte checksum.");

			uint64_t sum = 0;
			while (!big.empty()) {
				sum += big.peek();
				big.skip(1);
			}

			sum /= FILL_NUMBER * sizeof fill * FILL_BYTE;
			if (sum == 1)
				_.pass();
		}
	}

	return (0);
}
<commit_msg>Do a faster checksum.<commit_after>#include <common/buffer.h>
#include <common/test.h>

#define	FILL_BYTE	(0xa5)
#define	FILL_NUMBER	(1024)
#define	FILL_LENGTH	(242)

int
main(void)
{
	TestGroup g("/test/buffer/cut1", "Buffer::cut #1");

	unsigned n;
	for (n = 1; n < 12; n++) {
		Buffer big;

		big.append("Hello, ");
		size_t headerlen = big.length();

		uint8_t fill[n * FILL_LENGTH];
		unsigned i;
		for (i = 0; i < sizeof fill; i++) {
			fill[i] = FILL_BYTE;
		}

		for (i = 0; i < FILL_NUMBER; i++) {
			big.append(fill, sizeof fill);
		}

		big.append("world!\n");

		{
			Test _(g, "Cut fill bytes.");

			Buffer small(big);
			small.cut(headerlen, FILL_NUMBER * sizeof fill);
			if (small.equal("Hello, world!\n"))
				_.pass();
		}

		{
			Test _(g, "Proxy skip and trim.");

			big.cut(0, headerlen);
			big.cut(FILL_NUMBER * sizeof fill,
				big.length() - (FILL_NUMBER * sizeof fill));
			if (big.length() == FILL_NUMBER * sizeof fill)
				_.pass();
		}

		{
			Test _(g, "Correct fill byte checksum.");

			uint64_t sum = 0;
			Buffer::SegmentIterator iter = big.segments();
			while (!iter.end()) {
				const BufferSegment *seg = *iter;
				const uint8_t *p;
				const uint8_t *q = seg->end();
				for (p = seg->data(); p < q; p++)
					sum += *p;
				iter.next();
			}
			big.clear();

			sum /= FILL_NUMBER * sizeof fill * FILL_BYTE;
			if (sum == 1)
				_.pass();
		}
	}

	return (0);
}
<|endoftext|>


<commit_before>#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "cmm_conn_bootstrapper.h"
#include "cmm_socket.private.h"
#include "csocket_mapping.h"
#include "debug.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

ConnBootstrapper::ConnBootstrapper(CMMSocketImpl *sk_,
                                   int bootstrap_sock_,
                                   const struct sockaddr *remote_addr_,
                                   socklen_t addrlen_)
    : sk(sk_), bootstrap_sock(bootstrap_sock_), status_(EINPROGRESS),
      addrlen(addrlen_)
{
    remote_addr = new char[addrlen];
    memcpy(remote_addr, remote_addr_, addrlen);
}

ConnBootstrapper::~ConnBootstrapper()
{
    delete [] remote_addr;
}

void
ConnBootstrapper::stop()
{
    dbgprintf("Aborting bootstrapper for multisocket %d\n",
              sk->sock);
    shutdown(bootstrap_sock, SHUT_RDWR);
}

void
ConnBootstrapper::Finish()
{
    close(bootstrap_sock);
    dbgprintf("Exiting.\n");
}

void ConnBootstrapper::Run()
{
    char name[MAX_NAME_LEN+1];
    memset(name, 0, MAX_NAME_LEN+1);
    snprintf(name, MAX_NAME_LEN, "Bootstrapper %d", sk->sock);
    set_thread_name(name);

    PthreadScopedRWLock sock_lock(&sk->my_lock, true);

    if (sk->non_blocking) {
        detach();
    }

    try {
        if (bootstrap_sock != -1) {
            /* we are accepting a connection */
            dbgprintf("Accepting connection; using socket %d "
                      "to bootstrap\n", bootstrap_sock);
            sk->recv_remote_listeners(bootstrap_sock);
            sk->send_local_listeners(bootstrap_sock);
        } else {
            /* we are connecting */
            assert(remote_addr);

            bootstrap_sock = socket(sk->sock_family, sk->sock_type,
                                    sk->sock_protocol);
            if (bootstrap_sock < 0) {
                return;
            }

            struct sockaddr *addr = (struct sockaddr *)remote_addr;
            int rc = connect(bootstrap_sock, addr, addrlen);
            if (rc < 0) {
                perror("connect");
                status_ = errno;
                dbgprintf("Error connecting bootstrap socket\n");
                throw rc;
            }

            dbgprintf("Initiating connection; using socket %d "
                      "to bootstrap\n", bootstrap_sock);
            sk->send_local_listeners(bootstrap_sock);
            sk->recv_remote_listeners(bootstrap_sock);
        }

        // no errors; must have succeeded
        status_ = 0;
    } catch (int error_rc) {
        if (status_ != EINPROGRESS) {
            status_ = ECONNREFUSED;
        }
        // fall through so write-select returns
    }

    char ch = 42;
    int rc = write(sk->write_ready_pipe[1], &ch, 1);
    assert(rc == 1);
}

bool
ConnBootstrapper::succeeded()
{
    return status_ == 0;
}

bool
ConnBootstrapper::done()
{
    return status_ != EINPROGRESS;
}

int
ConnBootstrapper::status()
{
    return status_;
}
<commit_msg>Fixed non_blocking/is_non_blocking issues<commit_after>#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "cmm_conn_bootstrapper.h"
#include "cmm_socket.private.h"
#include "csocket_mapping.h"
#include "debug.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

ConnBootstrapper::ConnBootstrapper(CMMSocketImpl *sk_,
                                   int bootstrap_sock_,
                                   const struct sockaddr *remote_addr_,
                                   socklen_t addrlen_)
    : sk(sk_), bootstrap_sock(bootstrap_sock_), status_(EINPROGRESS),
      addrlen(addrlen_)
{
    remote_addr = new char[addrlen];
    memcpy(remote_addr, remote_addr_, addrlen);
}

ConnBootstrapper::~ConnBootstrapper()
{
    delete [] remote_addr;
}

void
ConnBootstrapper::stop()
{
    dbgprintf("Aborting bootstrapper for multisocket %d\n",
              sk->sock);
    shutdown(bootstrap_sock, SHUT_RDWR);
}

void
ConnBootstrapper::Finish()
{
    close(bootstrap_sock);
    dbgprintf("Exiting.\n");
}

void ConnBootstrapper::Run()
{
    char name[MAX_NAME_LEN+1];
    memset(name, 0, MAX_NAME_LEN+1);
    snprintf(name, MAX_NAME_LEN, "Bootstrapper %d", sk->sock);
    set_thread_name(name);

    PthreadScopedRWLock sock_lock(&sk->my_lock, true);

    if (sk->is_non_blocking()) {
        detach();
    }

    try {
        if (bootstrap_sock != -1) {
            /* we are accepting a connection */
            dbgprintf("Accepting connection; using socket %d "
                      "to bootstrap\n", bootstrap_sock);
            sk->recv_remote_listeners(bootstrap_sock);
            sk->send_local_listeners(bootstrap_sock);
        } else {
            /* we are connecting */
            assert(remote_addr);

            bootstrap_sock = socket(sk->sock_family, sk->sock_type,
                                    sk->sock_protocol);
            if (bootstrap_sock < 0) {
                dbgprintf("Error creating bootstrap socket: %s\n",
                          strerror(errno));
                return;
            }

            struct sockaddr *addr = (struct sockaddr *)remote_addr;
            int rc = connect(bootstrap_sock, addr, addrlen);
            if (rc < 0) {
                status_ = errno;
                dbgprintf("Error connecting bootstrap socket: %s\n",
                          strerror(errno));
                throw rc;
            }

            dbgprintf("Initiating connection; using socket %d "
                      "to bootstrap\n", bootstrap_sock);
            sk->send_local_listeners(bootstrap_sock);
            sk->recv_remote_listeners(bootstrap_sock);
        }

        // no errors; must have succeeded
        status_ = 0;
    } catch (int error_rc) {
        if (status_ != EINPROGRESS) {
            status_ = ECONNREFUSED;
        }
        // fall through so write-select returns
    }

    char ch = 42;
    int rc = write(sk->write_ready_pipe[1], &ch, 1);
    assert(rc == 1);
}

bool
ConnBootstrapper::succeeded()
{
    return status_ == 0;
}

bool
ConnBootstrapper::done()
{
    return status_ != EINPROGRESS;
}

int
ConnBootstrapper::status()
{
    return status_;
}
<|endoftext|>


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

#include "src/profiling/memory/unwinding.h"

#include <sys/types.h>
#include <unistd.h>

#include <unwindstack/MachineArm.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/MachineMips.h>
#include <unwindstack/MachineMips64.h>
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsMips.h>
#include <unwindstack/RegsMips64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
#include <unwindstack/Unwinder.h>
#include <unwindstack/UserArm.h>
#include <unwindstack/UserArm64.h>
#include <unwindstack/UserMips.h>
#include <unwindstack/UserMips64.h>
#include <unwindstack/UserX86.h>
#include <unwindstack/UserX86_64.h>

#include <procinfo/process_map.h>

#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/string_utils.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/base/thread_task_runner.h"
#include "src/profiling/memory/wire_protocol.h"

namespace perfetto {
namespace profiling {
namespace {

size_t kMaxFrames = 1000;

#pragma GCC diagnostic push
// We do not care about deterministic destructor order.
#pragma GCC diagnostic ignored "-Wglobal-constructors"
#pragma GCC diagnostic ignored "-Wexit-time-destructors"
static std::vector<std::string> kSkipMaps{"heapprofd_client.so"};
#pragma GCC diagnostic pop

std::unique_ptr<unwindstack::Regs> CreateFromRawData(unwindstack::ArchEnum arch,
                                                     void* raw_data) {
  std::unique_ptr<unwindstack::Regs> ret;
  // unwindstack::RegsX::Read returns a raw ptr which we are expected to free.
  switch (arch) {
    case unwindstack::ARCH_X86:
      ret.reset(unwindstack::RegsX86::Read(raw_data));
      break;
    case unwindstack::ARCH_X86_64:
      ret.reset(unwindstack::RegsX86_64::Read(raw_data));
      break;
    case unwindstack::ARCH_ARM:
      ret.reset(unwindstack::RegsArm::Read(raw_data));
      break;
    case unwindstack::ARCH_ARM64:
      ret.reset(unwindstack::RegsArm64::Read(raw_data));
      break;
    case unwindstack::ARCH_MIPS:
      ret.reset(unwindstack::RegsMips::Read(raw_data));
      break;
    case unwindstack::ARCH_MIPS64:
      ret.reset(unwindstack::RegsMips64::Read(raw_data));
      break;
    case unwindstack::ARCH_UNKNOWN:
      ret.reset(nullptr);
      break;
  }
  return ret;
}

// Behaves as a pread64, emulating it if not already exposed by the standard
// library. Safe to use on 32bit platforms for addresses with the top bit set.
// Clobbers the |fd| seek position if emulating.
ssize_t ReadAtOffsetClobberSeekPos(int fd,
                                   void* buf,
                                   size_t count,
                                   uint64_t addr) {
#ifdef __BIONIC__
  return pread64(fd, buf, count, static_cast<off64_t>(addr));
#else
  if (lseek64(fd, static_cast<off64_t>(addr), SEEK_SET) == -1)
    return -1;
  return read(fd, buf, count);
#endif
}

}  // namespace

StackOverlayMemory::StackOverlayMemory(std::shared_ptr<unwindstack::Memory> mem,
                                       uint64_t sp,
                                       uint8_t* stack,
                                       size_t size)
    : mem_(std::move(mem)), sp_(sp), stack_end_(sp + size), stack_(stack) {}

size_t StackOverlayMemory::Read(uint64_t addr, void* dst, size_t size) {
  if (addr >= sp_ && addr + size <= stack_end_ && addr + size > sp_) {
    size_t offset = static_cast<size_t>(addr - sp_);
    memcpy(dst, stack_ + offset, size);
    return size;
  }

  return mem_->Read(addr, dst, size);
}

FDMemory::FDMemory(base::ScopedFile mem_fd) : mem_fd_(std::move(mem_fd)) {}

size_t FDMemory::Read(uint64_t addr, void* dst, size_t size) {
  ssize_t rd = ReadAtOffsetClobberSeekPos(*mem_fd_, dst, size, addr);
  if (rd == -1) {
    PERFETTO_DPLOG("read of %zu at offset %" PRIu64, size, addr);
    return 0;
  }
  return static_cast<size_t>(rd);
}

FileDescriptorMaps::FileDescriptorMaps(base::ScopedFile fd)
    : fd_(std::move(fd)) {}

bool FileDescriptorMaps::Parse() {
  // If the process has already exited, lseek or ReadFileDescriptor will
  // return false.
  if (lseek(*fd_, 0, SEEK_SET) == -1)
    return false;

  std::string content;
  if (!base::ReadFileDescriptor(*fd_, &content))
    return false;
  return android::procinfo::ReadMapFileContent(
      &content[0], [&](uint64_t start, uint64_t end, uint16_t flags,
                       uint64_t pgoff, ino_t, const char* name) {
        // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
        if (strncmp(name, "/dev/", 5) == 0 &&
            strncmp(name + 5, "ashmem/", 7) != 0) {
          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
        }
        maps_.emplace_back(
            new unwindstack::MapInfo(nullptr, start, end, pgoff, flags, name));
      });
}

void FileDescriptorMaps::Reset() {
  maps_.clear();
}

bool DoUnwind(WireMessage* msg, UnwindingMetadata* metadata, AllocRecord* out) {
  AllocMetadata* alloc_metadata = msg->alloc_header;
  std::unique_ptr<unwindstack::Regs> regs(
      CreateFromRawData(alloc_metadata->arch, alloc_metadata->register_data));
  if (regs == nullptr) {
    PERFETTO_DLOG("Unable to construct unwindstack::Regs");
    unwindstack::FrameData frame_data{};
    frame_data.function_name = "ERROR READING REGISTERS";
    frame_data.map_name = "ERROR";

    out->frames.emplace_back(frame_data, "");
    return false;
  }
  uint8_t* stack = reinterpret_cast<uint8_t*>(msg->payload);
  std::shared_ptr<unwindstack::Memory> mems =
      std::make_shared<StackOverlayMemory>(metadata->fd_mem,
                                           alloc_metadata->stack_pointer, stack,
                                           msg->payload_size);

  unwindstack::Unwinder unwinder(kMaxFrames, &metadata->maps, regs.get(), mems);
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
  unwinder.SetJitDebug(metadata->jit_debug.get(), regs->Arch());
  unwinder.SetDexFiles(metadata->dex_files.get(), regs->Arch());
#endif
  // Surpress incorrect "variable may be uninitialized" error for if condition
  // after this loop. error_code = LastErrorCode gets run at least once.
  uint8_t error_code = 0;
  for (int attempt = 0; attempt < 2; ++attempt) {
    if (attempt > 0) {
      PERFETTO_DLOG("Reparsing maps");
      metadata->ReparseMaps();
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
      unwinder.SetJitDebug(metadata->jit_debug.get(), regs->Arch());
      unwinder.SetDexFiles(metadata->dex_files.get(), regs->Arch());
#endif
    }
    unwinder.Unwind(&kSkipMaps, nullptr);
    error_code = unwinder.LastErrorCode();
    if (error_code != unwindstack::ERROR_INVALID_MAP)
      break;
  }
  std::vector<unwindstack::FrameData> frames = unwinder.ConsumeFrames();
  for (unwindstack::FrameData& fd : frames) {
    std::string build_id;
    if (fd.map_name != "") {
      unwindstack::MapInfo* map_info = metadata->maps.Find(fd.pc);
      if (map_info)
        build_id = map_info->GetBuildID();
    }

    out->frames.emplace_back(std::move(fd), std::move(build_id));
  }

  if (error_code != 0) {
    PERFETTO_DLOG("Unwinding error %" PRIu8, error_code);
    unwindstack::FrameData frame_data{};
    frame_data.function_name = "ERROR " + std::to_string(error_code);
    frame_data.map_name = "ERROR";

    out->frames.emplace_back(frame_data, "");
  }

  return true;
}

void UnwindingWorker::OnDisconnect(base::UnixSocket* self) {
  // TODO(fmayer): Maybe try to drain shmem one last time.
  auto it = client_data_.find(self->peer_pid());
  if (it == client_data_.end()) {
    PERFETTO_DFATAL("Disconnected unexpecter socket.");
    return;
  }
  ClientData& client_data = it->second;
  DataSourceInstanceID ds_id = client_data.data_source_instance_id;
  client_data_.erase(it);
  delegate_->PostSocketDisconnected(ds_id, self->peer_pid());
}

void UnwindingWorker::OnDataAvailable(base::UnixSocket* self) {
  // Drain buffer to clear the notification.
  char recv_buf[1024];
  self->Receive(recv_buf, sizeof(recv_buf));

  auto it = client_data_.find(self->peer_pid());
  if (it == client_data_.end()) {
    PERFETTO_DFATAL("Unexpected data.");
    return;
  }

  ClientData& client_data = it->second;
  SharedRingBuffer& shmem = client_data.shmem;
  SharedRingBuffer::Buffer buf;

  for (;;) {
    // TODO(fmayer): Allow spinlock acquisition to fail and repost Task if it
    // did.
    buf = shmem.BeginRead();
    if (!buf)
      break;
    HandleBuffer(buf, &client_data.metadata,
                 client_data.data_source_instance_id,
                 client_data.sock->peer_pid(), delegate_);
    shmem.EndRead(std::move(buf));
  }
}

// static
void UnwindingWorker::HandleBuffer(const SharedRingBuffer::Buffer& buf,
                                   UnwindingMetadata* unwinding_metadata,
                                   DataSourceInstanceID data_source_instance_id,
                                   pid_t peer_pid,
                                   Delegate* delegate) {
  WireMessage msg;
  // TODO(fmayer): standardise on char* or uint8_t*.
  // char* has stronger guarantees regarding aliasing.
  // see https://timsong-cpp.github.io/cppwp/n3337/basic.lval#10.8
  if (!ReceiveWireMessage(reinterpret_cast<char*>(buf.data), buf.size, &msg)) {
    PERFETTO_DFATAL("Failed to receive wire message.");
    return;
  }

  if (msg.record_type == RecordType::Malloc) {
    AllocRecord rec;
    rec.alloc_metadata = *msg.alloc_header;
    rec.pid = peer_pid;
    rec.data_source_instance_id = data_source_instance_id;
    DoUnwind(&msg, unwinding_metadata, &rec);
    delegate->PostAllocRecord(std::move(rec));
  } else if (msg.record_type == RecordType::Free) {
    FreeRecord rec;
    rec.pid = peer_pid;
    rec.data_source_instance_id = data_source_instance_id;
    // We need to copy this, so we can return the memory to the shmem buffer.
    memcpy(&rec.free_batch, msg.free_header, sizeof(*msg.free_header));
    delegate->PostFreeRecord(std::move(rec));
  } else {
    PERFETTO_DFATAL("Invalid record type.");
  }
}

void UnwindingWorker::PostHandoffSocket(HandoffData handoff_data) {
  // Even with C++14, this cannot be moved, as std::function has to be
  // copyable, which HandoffData is not.
  HandoffData* raw_data = new HandoffData(std::move(handoff_data));
  // We do not need to use a WeakPtr here because the task runner will not
  // outlive its UnwindingWorker.
  thread_task_runner_.get()->PostTask([this, raw_data] {
    HandoffData data = std::move(*raw_data);
    delete raw_data;
    HandleHandoffSocket(std::move(data));
  });
}

void UnwindingWorker::HandleHandoffSocket(HandoffData handoff_data) {
  auto sock = base::UnixSocket::AdoptConnected(
      handoff_data.sock.ReleaseFd(), this, this->thread_task_runner_.get(),
      base::SockType::kStream);
  pid_t peer_pid = sock->peer_pid();

  UnwindingMetadata metadata(peer_pid,
                             std::move(handoff_data.fds[kHandshakeMaps]),
                             std::move(handoff_data.fds[kHandshakeMem]));
  ClientData client_data{
      handoff_data.data_source_instance_id, std::move(sock),
      std::move(metadata), std::move(handoff_data.shmem),
  };
  client_data_.emplace(peer_pid, std::move(client_data));
}

void UnwindingWorker::PostDisconnectSocket(pid_t pid) {
  // We do not need to use a WeakPtr here because the task runner will not
  // outlive its UnwindingWorker.
  thread_task_runner_.get()->PostTask(
      [this, pid] { HandleDisconnectSocket(pid); });
}

void UnwindingWorker::HandleDisconnectSocket(pid_t pid) {
  client_data_.erase(pid);
}

UnwindingWorker::Delegate::~Delegate() = default;

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

#include "src/profiling/memory/unwinding.h"

#include <sys/types.h>
#include <unistd.h>

#include <unwindstack/MachineArm.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/MachineMips.h>
#include <unwindstack/MachineMips64.h>
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsMips.h>
#include <unwindstack/RegsMips64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
#include <unwindstack/Unwinder.h>
#include <unwindstack/UserArm.h>
#include <unwindstack/UserArm64.h>
#include <unwindstack/UserMips.h>
#include <unwindstack/UserMips64.h>
#include <unwindstack/UserX86.h>
#include <unwindstack/UserX86_64.h>

#include <procinfo/process_map.h>

#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/string_utils.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/base/thread_task_runner.h"
#include "src/profiling/memory/wire_protocol.h"

namespace perfetto {
namespace profiling {
namespace {

size_t kMaxFrames = 1000;

#pragma GCC diagnostic push
// We do not care about deterministic destructor order.
#pragma GCC diagnostic ignored "-Wglobal-constructors"
#pragma GCC diagnostic ignored "-Wexit-time-destructors"
static std::vector<std::string> kSkipMaps{"heapprofd_client.so"};
#pragma GCC diagnostic pop

std::unique_ptr<unwindstack::Regs> CreateFromRawData(unwindstack::ArchEnum arch,
                                                     void* raw_data) {
  std::unique_ptr<unwindstack::Regs> ret;
  // unwindstack::RegsX::Read returns a raw ptr which we are expected to free.
  switch (arch) {
    case unwindstack::ARCH_X86:
      ret.reset(unwindstack::RegsX86::Read(raw_data));
      break;
    case unwindstack::ARCH_X86_64:
      ret.reset(unwindstack::RegsX86_64::Read(raw_data));
      break;
    case unwindstack::ARCH_ARM:
      ret.reset(unwindstack::RegsArm::Read(raw_data));
      break;
    case unwindstack::ARCH_ARM64:
      ret.reset(unwindstack::RegsArm64::Read(raw_data));
      break;
    case unwindstack::ARCH_MIPS:
      ret.reset(unwindstack::RegsMips::Read(raw_data));
      break;
    case unwindstack::ARCH_MIPS64:
      ret.reset(unwindstack::RegsMips64::Read(raw_data));
      break;
    case unwindstack::ARCH_UNKNOWN:
      ret.reset(nullptr);
      break;
  }
  return ret;
}

// Behaves as a pread64, emulating it if not already exposed by the standard
// library. Safe to use on 32bit platforms for addresses with the top bit set.
// Clobbers the |fd| seek position if emulating.
ssize_t ReadAtOffsetClobberSeekPos(int fd,
                                   void* buf,
                                   size_t count,
                                   uint64_t addr) {
#ifdef __BIONIC__
  return pread64(fd, buf, count, static_cast<off64_t>(addr));
#else
  if (lseek64(fd, static_cast<off64_t>(addr), SEEK_SET) == -1)
    return -1;
  return read(fd, buf, count);
#endif
}

}  // namespace

StackOverlayMemory::StackOverlayMemory(std::shared_ptr<unwindstack::Memory> mem,
                                       uint64_t sp,
                                       uint8_t* stack,
                                       size_t size)
    : mem_(std::move(mem)), sp_(sp), stack_end_(sp + size), stack_(stack) {}

size_t StackOverlayMemory::Read(uint64_t addr, void* dst, size_t size) {
  if (addr >= sp_ && addr + size <= stack_end_ && addr + size > sp_) {
    size_t offset = static_cast<size_t>(addr - sp_);
    memcpy(dst, stack_ + offset, size);
    return size;
  }

  return mem_->Read(addr, dst, size);
}

FDMemory::FDMemory(base::ScopedFile mem_fd) : mem_fd_(std::move(mem_fd)) {}

size_t FDMemory::Read(uint64_t addr, void* dst, size_t size) {
  ssize_t rd = ReadAtOffsetClobberSeekPos(*mem_fd_, dst, size, addr);
  if (rd == -1) {
    PERFETTO_DPLOG("read of %zu at offset %" PRIu64, size, addr);
    return 0;
  }
  return static_cast<size_t>(rd);
}

FileDescriptorMaps::FileDescriptorMaps(base::ScopedFile fd)
    : fd_(std::move(fd)) {}

bool FileDescriptorMaps::Parse() {
  // If the process has already exited, lseek or ReadFileDescriptor will
  // return false.
  if (lseek(*fd_, 0, SEEK_SET) == -1)
    return false;

  std::string content;
  if (!base::ReadFileDescriptor(*fd_, &content))
    return false;
  return android::procinfo::ReadMapFileContent(
      &content[0], [&](uint64_t start, uint64_t end, uint16_t flags,
                       uint64_t pgoff, ino_t, const char* name) {
        // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
        if (strncmp(name, "/dev/", 5) == 0 &&
            strncmp(name + 5, "ashmem/", 7) != 0) {
          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
        }
        unwindstack::MapInfo* prev_map =
            maps_.empty() ? nullptr : maps_.back().get();
        maps_.emplace_back(
            new unwindstack::MapInfo(prev_map, start, end, pgoff, flags, name));
      });
}

void FileDescriptorMaps::Reset() {
  maps_.clear();
}

bool DoUnwind(WireMessage* msg, UnwindingMetadata* metadata, AllocRecord* out) {
  AllocMetadata* alloc_metadata = msg->alloc_header;
  std::unique_ptr<unwindstack::Regs> regs(
      CreateFromRawData(alloc_metadata->arch, alloc_metadata->register_data));
  if (regs == nullptr) {
    PERFETTO_DLOG("Unable to construct unwindstack::Regs");
    unwindstack::FrameData frame_data{};
    frame_data.function_name = "ERROR READING REGISTERS";
    frame_data.map_name = "ERROR";

    out->frames.emplace_back(frame_data, "");
    return false;
  }
  uint8_t* stack = reinterpret_cast<uint8_t*>(msg->payload);
  std::shared_ptr<unwindstack::Memory> mems =
      std::make_shared<StackOverlayMemory>(metadata->fd_mem,
                                           alloc_metadata->stack_pointer, stack,
                                           msg->payload_size);

  unwindstack::Unwinder unwinder(kMaxFrames, &metadata->maps, regs.get(), mems);
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
  unwinder.SetJitDebug(metadata->jit_debug.get(), regs->Arch());
  unwinder.SetDexFiles(metadata->dex_files.get(), regs->Arch());
#endif
  // Surpress incorrect "variable may be uninitialized" error for if condition
  // after this loop. error_code = LastErrorCode gets run at least once.
  uint8_t error_code = 0;
  for (int attempt = 0; attempt < 2; ++attempt) {
    if (attempt > 0) {
      PERFETTO_DLOG("Reparsing maps");
      metadata->ReparseMaps();
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
      unwinder.SetJitDebug(metadata->jit_debug.get(), regs->Arch());
      unwinder.SetDexFiles(metadata->dex_files.get(), regs->Arch());
#endif
    }
    unwinder.Unwind(&kSkipMaps, nullptr);
    error_code = unwinder.LastErrorCode();
    if (error_code != unwindstack::ERROR_INVALID_MAP)
      break;
  }
  std::vector<unwindstack::FrameData> frames = unwinder.ConsumeFrames();
  for (unwindstack::FrameData& fd : frames) {
    std::string build_id;
    if (fd.map_name != "") {
      unwindstack::MapInfo* map_info = metadata->maps.Find(fd.pc);
      if (map_info)
        build_id = map_info->GetBuildID();
    }

    out->frames.emplace_back(std::move(fd), std::move(build_id));
  }

  if (error_code != 0) {
    PERFETTO_DLOG("Unwinding error %" PRIu8, error_code);
    unwindstack::FrameData frame_data{};
    frame_data.function_name = "ERROR " + std::to_string(error_code);
    frame_data.map_name = "ERROR";

    out->frames.emplace_back(frame_data, "");
  }

  return true;
}

void UnwindingWorker::OnDisconnect(base::UnixSocket* self) {
  // TODO(fmayer): Maybe try to drain shmem one last time.
  auto it = client_data_.find(self->peer_pid());
  if (it == client_data_.end()) {
    PERFETTO_DFATAL("Disconnected unexpecter socket.");
    return;
  }
  ClientData& client_data = it->second;
  DataSourceInstanceID ds_id = client_data.data_source_instance_id;
  client_data_.erase(it);
  delegate_->PostSocketDisconnected(ds_id, self->peer_pid());
}

void UnwindingWorker::OnDataAvailable(base::UnixSocket* self) {
  // Drain buffer to clear the notification.
  char recv_buf[1024];
  self->Receive(recv_buf, sizeof(recv_buf));

  auto it = client_data_.find(self->peer_pid());
  if (it == client_data_.end()) {
    PERFETTO_DFATAL("Unexpected data.");
    return;
  }

  ClientData& client_data = it->second;
  SharedRingBuffer& shmem = client_data.shmem;
  SharedRingBuffer::Buffer buf;

  for (;;) {
    // TODO(fmayer): Allow spinlock acquisition to fail and repost Task if it
    // did.
    buf = shmem.BeginRead();
    if (!buf)
      break;
    HandleBuffer(buf, &client_data.metadata,
                 client_data.data_source_instance_id,
                 client_data.sock->peer_pid(), delegate_);
    shmem.EndRead(std::move(buf));
  }
}

// static
void UnwindingWorker::HandleBuffer(const SharedRingBuffer::Buffer& buf,
                                   UnwindingMetadata* unwinding_metadata,
                                   DataSourceInstanceID data_source_instance_id,
                                   pid_t peer_pid,
                                   Delegate* delegate) {
  WireMessage msg;
  // TODO(fmayer): standardise on char* or uint8_t*.
  // char* has stronger guarantees regarding aliasing.
  // see https://timsong-cpp.github.io/cppwp/n3337/basic.lval#10.8
  if (!ReceiveWireMessage(reinterpret_cast<char*>(buf.data), buf.size, &msg)) {
    PERFETTO_DFATAL("Failed to receive wire message.");
    return;
  }

  if (msg.record_type == RecordType::Malloc) {
    AllocRecord rec;
    rec.alloc_metadata = *msg.alloc_header;
    rec.pid = peer_pid;
    rec.data_source_instance_id = data_source_instance_id;
    DoUnwind(&msg, unwinding_metadata, &rec);
    delegate->PostAllocRecord(std::move(rec));
  } else if (msg.record_type == RecordType::Free) {
    FreeRecord rec;
    rec.pid = peer_pid;
    rec.data_source_instance_id = data_source_instance_id;
    // We need to copy this, so we can return the memory to the shmem buffer.
    memcpy(&rec.free_batch, msg.free_header, sizeof(*msg.free_header));
    delegate->PostFreeRecord(std::move(rec));
  } else {
    PERFETTO_DFATAL("Invalid record type.");
  }
}

void UnwindingWorker::PostHandoffSocket(HandoffData handoff_data) {
  // Even with C++14, this cannot be moved, as std::function has to be
  // copyable, which HandoffData is not.
  HandoffData* raw_data = new HandoffData(std::move(handoff_data));
  // We do not need to use a WeakPtr here because the task runner will not
  // outlive its UnwindingWorker.
  thread_task_runner_.get()->PostTask([this, raw_data] {
    HandoffData data = std::move(*raw_data);
    delete raw_data;
    HandleHandoffSocket(std::move(data));
  });
}

void UnwindingWorker::HandleHandoffSocket(HandoffData handoff_data) {
  auto sock = base::UnixSocket::AdoptConnected(
      handoff_data.sock.ReleaseFd(), this, this->thread_task_runner_.get(),
      base::SockType::kStream);
  pid_t peer_pid = sock->peer_pid();

  UnwindingMetadata metadata(peer_pid,
                             std::move(handoff_data.fds[kHandshakeMaps]),
                             std::move(handoff_data.fds[kHandshakeMem]));
  ClientData client_data{
      handoff_data.data_source_instance_id, std::move(sock),
      std::move(metadata), std::move(handoff_data.shmem),
  };
  client_data_.emplace(peer_pid, std::move(client_data));
}

void UnwindingWorker::PostDisconnectSocket(pid_t pid) {
  // We do not need to use a WeakPtr here because the task runner will not
  // outlive its UnwindingWorker.
  thread_task_runner_.get()->PostTask(
      [this, pid] { HandleDisconnectSocket(pid); });
}

void UnwindingWorker::HandleDisconnectSocket(pid_t pid) {
  client_data_.erase(pid);
}

UnwindingWorker::Delegate::~Delegate() = default;

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


<commit_before>/**
Copyright (c) 2015, Intel Corporation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Intel Corporation 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 INTEL CORPORATION 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 <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>

#include <omp.h>

#include "CSR.hpp"
#include "COO.hpp"
#include "mm_io.h"
#include "Utils.hpp"
#include "MemoryPool.hpp"

#ifdef LOADIMBA
#include "synk/loadimba.hpp"
#else
#include "synk/barrier.hpp"
#endif

using namespace std;

#ifdef LOADIMBA
extern synk::LoadImba *bar;
#else
extern synk::Barrier *bar;
#endif

namespace SpMP
{

bool CSR::useMemoryPool_() const
{
  return MemoryPool::getSingleton()->contains(rowptr);
}

CSR::CSR() : rowptr(NULL), colidx(NULL), values(NULL), idiag(NULL), diag(NULL), diagptr(NULL), extptr(NULL), base(0), ownData_(false)
{
}

void CSR::alloc(int m, int nnz, bool createSeparateDiagData /*= true*/)
{
  this->m = m;

  rowptr = MALLOC(int, m + 1);
  colidx = MALLOC(int, nnz);
  values = MALLOC(double, nnz);
  diagptr = MALLOC(int, m);

  assert(rowptr != NULL);
  assert(colidx != NULL);
  assert(values != NULL);
  assert(diagptr != NULL);

  if (createSeparateDiagData) {
    idiag = MALLOC(double, m);
    diag = MALLOC(double, m);
    assert(idiag != NULL);
    assert(diag != NULL);
  }

  ownData_ = true;
}

CSR::CSR(int m, int n, int nnz, int base /*=0*/)
 : base(base)
{
  this->m=m;
  this->n=n;
  alloc(n, nnz);
}

CSR::CSR(const char *file, bool forceSymmetric /*=false*/, int pad /*=1*/)
 : base(0), rowptr(NULL), colidx(NULL), values(NULL), idiag(NULL), diag(NULL), diagptr(NULL), extptr(NULL)
{
  int m = atoi(file);
  char buf[1024];
  sprintf(buf, "%d", m);

  int l = strlen(file);

  if (!strcmp(buf, file)) {
    generate3D27PtLaplacian(this, m);
  }
  else {
    COO Acoo;
    load_matrix_market((char *)file, Acoo, forceSymmetric, pad);

    alloc(Acoo.m, Acoo.nnz);

    dcoo2crs(&Acoo, this);
  }
}

CSR::CSR(int m, int n, int *rowptr, int *colidx, double *values, int base /*=0*/) :
 m(m), n(n), rowptr(rowptr), colidx(colidx), values(values), ownData_(false), idiag(NULL), diag(NULL), extptr(NULL)
{
  diagptr = MALLOC(int, m);
#pragma omp parallel for
  for (int i = 0; i < m; ++i) {
    for (int j = rowptr[i] - base; j < rowptr[i + 1] - base; ++j) {
      if (colidx[j] - base == i) {
        diagptr[i] = j + base;
      }
    }
  }
}

void CSR::dealloc()
{
  if (useMemoryPool_()) {
    // a large single contiguous chunk is allocated to
    // buffers except rowptr and colidx.
    rowptr = NULL;
    extptr = NULL;
    colidx = NULL;
    values = NULL;
    idiag = NULL;
    diag = NULL;
    diagptr = NULL;
  }
  else {
    if (ownData_) {
      FREE(rowptr);
      FREE(extptr);
      FREE(colidx);
      FREE(values);
    }

    FREE(idiag);
    FREE(diag);
    FREE(diagptr);
  }
}

CSR::~CSR()
{
  dealloc();
}

bool CSR::isSymmetric(bool checkValues /*=true*/, bool printFirstNonSymmetry /* = false*/) const
{
  const int *extptr = this->extptr ? this->extptr : rowptr + 1;
  for (int i = 0; i < m; ++i) {
    for (int j = rowptr[i] - base; j < extptr[i] - base; ++j) {
      int c = colidx[j] - base;
      if (c > i) {
        bool hasPair = false;
        for (int k = rowptr[c] - base; k < extptr[c] - base; ++k) {
          if (colidx[k] - base == i) {
            hasPair = true;
            if (checkValues && values[j] != values[k]) {
              if (printFirstNonSymmetry) {
                printf(
                  "assymmetric (%d, %d) = %g, (%d, %d) = %g\n",
                  i + 1, c + 1, values[j], c + 1, i + 1, values[k]);
              }
              return false;
            }
            break;
          }
        }
        if (!hasPair) {
          if (printFirstNonSymmetry) {
            printf(
              "assymmetric (%d, %d) exists but (%d, %d) doesn't\n",
              i + 1, c + 1, c + 1, i + 1);
          }
          return false;
        }
      }
    }
  } // for each row

  return true;
}

void CSR::store_matrix_market(const char *file_name) const
{
  FILE *fp = fopen(file_name, "w");
  assert(fp);

  MM_typecode matcode;
  mm_initialize_typecode(&matcode);
  mm_set_matrix(&matcode);
  mm_set_sparse(&matcode);
  mm_set_real(&matcode);

  // print banner followed by typecode.
  fprintf(fp, "%s ", MatrixMarketBanner);
  fprintf(fp, "%s\n", mm_typecode_to_str(matcode));

  // print matrix size and nonzeros.
  fprintf(fp, "%d %d %d\n", m, n, rowptr[m]);

  // print values
  for (int i = 0; i < m; ++i) {
    for (int j = rowptr[i]; j < rowptr[i + 1]; ++j) {
      fprintf(fp, "%d %d %20.16g\n", i + 1, colidx[j] + 1, values[j]);
    }
  }

  fclose(fp);
}

void CSR::make0BasedIndexing()
{
  if (0 == base) return;

#pragma omp parallel for
  for(int i=0; i <= m; i++)
    rowptr[i]--;

  int nnz = rowptr[m];
#pragma omp parallel for
  for(int i=0; i < nnz; i++)
    colidx[i]--;

  base = 0;
}

void CSR::make1BasedIndexing()
{
  if (1 == base) return;

#pragma omp parallel for
  for(int i=0; i <= m; i++)
    rowptr[i]++;

  int nnz = rowptr[m];
#pragma omp parallel for
  for(int i=0; i < nnz; i++)
    colidx[i]++;

  base = 1;
}

/**
 * idx = idx2*dim1 + idx1
 * -> ret = idx1*dim2 + idx2
 *        = (idx%dim1)*dim2 + idx/dim1
 */
static inline int transpose_idx(int idx, int dim1, int dim2)
{
  return idx%dim1*dim2 + idx/dim1;
}

// TODO: only supports local matrix for now
/**
 * Transposition using parallel counting sort
 */
CSR *CSR::transpose() const
{
   const double *A_data = values;
   const int *A_i = rowptr;
   const int *A_j = colidx;
   int num_rowsA = m;
   int num_colsA = n;
   int nnz = rowptr[m];
   int num_nonzerosA = nnz;

   CSR *AT = new CSR();

   AT->m = m;
   AT->n = n;
   AT->colidx = MALLOC(int, nnz);
   assert(AT->colidx);
   if (A_data) {
     AT->values = MALLOC(double, nnz);
     assert(AT->values);
   }
   if (diagptr) {
     AT->diagptr = MALLOC(int, m);
     assert(AT->diagptr);
   }

   if (0 == num_colsA) {
     return AT;
   }

   int *AT_j = AT->colidx;
   double *AT_data = AT->values;

   double t = omp_get_wtime();

   int *bucket = MALLOC(
    int, (num_colsA + 1)*omp_get_max_threads());

#ifndef NDEBUG
  int i;
  for (i = 0; i < num_rowsA; ++i) {
    assert(A_i[i + 1] >= A_i[i]);
  }
#endif

#define MIN(a, b) (((a) <= (b)) ? (a) : (b))

#pragma omp parallel
   {
   int nthreads = omp_get_num_threads();
   int tid = omp_get_thread_num();

   int nnzPerThread = (num_nonzerosA + nthreads - 1)/nthreads;
   int iBegin = lower_bound(A_i, A_i + num_rowsA, nnzPerThread*tid) - A_i;
   int iEnd = lower_bound(A_i, A_i + num_rowsA, nnzPerThread*(tid + 1)) - A_i;

   int i, j;
   memset(bucket + tid*num_colsA, 0, sizeof(int)*num_colsA);

   // count the number of keys that will go into each bucket
   for (j = A_i[iBegin]; j < A_i[iEnd]; ++j) {
     int idx = A_j[j];
#ifndef NDEBUG
     if (idx < 0 || idx >= num_colsA) {
       printf("tid = %d num_rowsA = %d num_colsA = %d num_nonzerosA = %d iBegin = %d iEnd = %d A_i[iBegin] = %d A_i[iEnd] = %d j = %d idx = %d\n", tid, num_rowsA, num_colsA, num_nonzerosA, iBegin, iEnd, A_i[iBegin], A_i[iEnd], j, idx);
     }
#endif
     assert(idx >= 0 && idx < num_colsA);
     bucket[tid*num_colsA + idx]++;
   }
   // up to here, bucket is used as int[nthreads][num_colsA] 2D array

   // prefix sum
#pragma omp barrier

   for (i = tid*num_colsA + 1; i < (tid + 1)*num_colsA; ++i) {
     int transpose_i = transpose_idx(i, nthreads, num_colsA);
     int transpose_i_minus_1 = transpose_idx(i - 1, nthreads, num_colsA);

     bucket[transpose_i] += bucket[transpose_i_minus_1];
   }

#pragma omp barrier
#pragma omp master
   {
     for (i = 1; i < nthreads; ++i) {
       int j0 = num_colsA*i - 1, j1 = num_colsA*(i + 1) - 1;
       int transpose_j0 = transpose_idx(j0, nthreads, num_colsA);
       int transpose_j1 = transpose_idx(j1, nthreads, num_colsA);

       bucket[transpose_j1] += bucket[transpose_j0];
     }
     bucket[num_colsA] = num_nonzerosA;
   }
#pragma omp barrier

   if (tid > 0) {
     int transpose_i0 = transpose_idx(num_colsA*tid - 1, nthreads, num_colsA);

     for (i = tid*num_colsA; i < (tid + 1)*num_colsA - 1; ++i) {
       int transpose_i = transpose_idx(i, nthreads, num_colsA);

       bucket[transpose_i] += bucket[transpose_i0];
     }
   }

#pragma omp barrier

   if (A_data) {
      for (i = iEnd - 1; i >= iBegin; --i) {
        for (j = A_i[i + 1] - 1; j >= A_i[i]; --j) {
          int idx = A_j[j];
          --bucket[tid*num_colsA + idx];

          int offset = bucket[tid*num_colsA + idx];

          assert(offset >= 0 && offset < num_nonzerosA);
          AT_data[offset] = A_data[j];
          AT_j[offset] = i;
        }
      }
   }
   else {
      for (i = iEnd - 1; i >= iBegin; --i) {
        for (j = A_i[i + 1] - 1; j >= A_i[i]; --j) {
          int idx = A_j[j];
          --bucket[tid*num_colsA + idx];

          int offset = bucket[tid*num_colsA + idx];

          AT_j[offset] = i;
        }
      }
   }

   if (diagptr) {
#pragma omp barrier
     for (i = iBegin; i < iEnd; ++i) {
       for (int j = bucket[i]; j < bucket[i + 1]; ++j) {
         int c = AT_j[j];
         if (c == i) AT->diagptr[i] = j;
       }
     }
   }

   } // omp parallel

   AT->rowptr = bucket;

   return AT;
 }

template<int BASE = 0>
int getBandwidth_(const CSR *A)
{
  int bw = INT_MIN;
#pragma omp parallel for reduction(max:bw)
  for (int i = 0; i < A->m; ++i) {
    for (int j = A->rowptr[i] - BASE; j < A->rowptr[i + 1] - BASE; ++j) {
      int c = A->colidx[j] - BASE;
      int temp = c - i;
      if (temp < 0) temp = -temp;
      bw = max(temp, bw);
    }
  }
  return bw;
}

int CSR::getBandwidth() const
{
  if (0 == base) {
    return getBandwidth_<0>(this);
  }
  else {
    assert(1 == base);
    return getBandwidth_<1>(this);
  }
}

} // namespace SpMP
<commit_msg>initialize base<commit_after>/**
Copyright (c) 2015, Intel Corporation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the Intel Corporation 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 INTEL CORPORATION 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 <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>

#include <omp.h>

#include "CSR.hpp"
#include "COO.hpp"
#include "mm_io.h"
#include "Utils.hpp"
#include "MemoryPool.hpp"

#ifdef LOADIMBA
#include "synk/loadimba.hpp"
#else
#include "synk/barrier.hpp"
#endif

using namespace std;

#ifdef LOADIMBA
extern synk::LoadImba *bar;
#else
extern synk::Barrier *bar;
#endif

namespace SpMP
{

bool CSR::useMemoryPool_() const
{
  return MemoryPool::getSingleton()->contains(rowptr);
}

CSR::CSR() : rowptr(NULL), colidx(NULL), values(NULL), idiag(NULL), diag(NULL), diagptr(NULL), extptr(NULL), base(0), ownData_(false)
{
}

void CSR::alloc(int m, int nnz, bool createSeparateDiagData /*= true*/)
{
  this->m = m;

  rowptr = MALLOC(int, m + 1);
  colidx = MALLOC(int, nnz);
  values = MALLOC(double, nnz);
  diagptr = MALLOC(int, m);

  assert(rowptr != NULL);
  assert(colidx != NULL);
  assert(values != NULL);
  assert(diagptr != NULL);

  if (createSeparateDiagData) {
    idiag = MALLOC(double, m);
    diag = MALLOC(double, m);
    assert(idiag != NULL);
    assert(diag != NULL);
  }

  ownData_ = true;
}

CSR::CSR(int m, int n, int nnz, int base /*=0*/)
 : base(base)
{
  this->m=m;
  this->n=n;
  alloc(n, nnz);
}

CSR::CSR(const char *file, bool forceSymmetric /*=false*/, int pad /*=1*/)
 : base(0), rowptr(NULL), colidx(NULL), values(NULL), idiag(NULL), diag(NULL), diagptr(NULL), extptr(NULL)
{
  int m = atoi(file);
  char buf[1024];
  sprintf(buf, "%d", m);

  int l = strlen(file);

  if (!strcmp(buf, file)) {
    generate3D27PtLaplacian(this, m);
  }
  else {
    COO Acoo;
    load_matrix_market((char *)file, Acoo, forceSymmetric, pad);

    alloc(Acoo.m, Acoo.nnz);

    dcoo2crs(&Acoo, this);
  }
}

CSR::CSR(int m, int n, int *rowptr, int *colidx, double *values, int base /*=0*/) :
 m(m), n(n), rowptr(rowptr), colidx(colidx), values(values), ownData_(false), idiag(NULL), diag(NULL), extptr(NULL), base(base)
{
  diagptr = MALLOC(int, m);
#pragma omp parallel for
  for (int i = 0; i < m; ++i) {
    for (int j = rowptr[i] - base; j < rowptr[i + 1] - base; ++j) {
      if (colidx[j] - base == i) {
        diagptr[i] = j + base;
      }
    }
  }
}

void CSR::dealloc()
{
  if (useMemoryPool_()) {
    // a large single contiguous chunk is allocated to
    // buffers except rowptr and colidx.
    rowptr = NULL;
    extptr = NULL;
    colidx = NULL;
    values = NULL;
    idiag = NULL;
    diag = NULL;
    diagptr = NULL;
  }
  else {
    if (ownData_) {
      FREE(rowptr);
      FREE(extptr);
      FREE(colidx);
      FREE(values);
    }

    FREE(idiag);
    FREE(diag);
    FREE(diagptr);
  }
}

CSR::~CSR()
{
  dealloc();
}

bool CSR::isSymmetric(bool checkValues /*=true*/, bool printFirstNonSymmetry /* = false*/) const
{
  const int *extptr = this->extptr ? this->extptr : rowptr + 1;
  for (int i = 0; i < m; ++i) {
    for (int j = rowptr[i] - base; j < extptr[i] - base; ++j) {
      int c = colidx[j] - base;
      if (c != i) {
        bool hasPair = false;
        for (int k = rowptr[c] - base; k < extptr[c] - base; ++k) {
          if (colidx[k] - base == i) {
            hasPair = true;
            if (checkValues && values[j] != values[k]) {
              if (printFirstNonSymmetry) {
                printf(
                  "assymmetric (%d, %d) = %g, (%d, %d) = %g\n",
                  i + 1, c + 1, values[j], c + 1, i + 1, values[k]);
              }
              return false;
            }
            break;
          }
        }
        if (!hasPair) {
          if (printFirstNonSymmetry) {
            printf(
              "assymmetric (%d, %d) exists but (%d, %d) doesn't\n",
              i + 1, c + 1, c + 1, i + 1);
          }
          return false;
        }
      }
    }
  } // for each row

  return true;
}

void CSR::store_matrix_market(const char *file_name) const
{
  FILE *fp = fopen(file_name, "w");
  assert(fp);

  MM_typecode matcode;
  mm_initialize_typecode(&matcode);
  mm_set_matrix(&matcode);
  mm_set_sparse(&matcode);
  mm_set_real(&matcode);

  // print banner followed by typecode.
  fprintf(fp, "%s ", MatrixMarketBanner);
  fprintf(fp, "%s\n", mm_typecode_to_str(matcode));

  // print matrix size and nonzeros.
  fprintf(fp, "%d %d %d\n", m, n, rowptr[m]);

  // print values
  for (int i = 0; i < m; ++i) {
    for (int j = rowptr[i]; j < rowptr[i + 1]; ++j) {
      fprintf(fp, "%d %d %20.16g\n", i + 1, colidx[j] + 1, values[j]);
    }
  }

  fclose(fp);
}

void CSR::make0BasedIndexing()
{
  if (0 == base) return;

#pragma omp parallel for
  for(int i=0; i <= m; i++)
    rowptr[i]--;

  int nnz = rowptr[m];
#pragma omp parallel for
  for(int i=0; i < nnz; i++)
    colidx[i]--;

  base = 0;
}

void CSR::make1BasedIndexing()
{
  if (1 == base) return;

#pragma omp parallel for
  for(int i=0; i <= m; i++)
    rowptr[i]++;

  int nnz = rowptr[m];
#pragma omp parallel for
  for(int i=0; i < nnz; i++)
    colidx[i]++;

  base = 1;
}

/**
 * idx = idx2*dim1 + idx1
 * -> ret = idx1*dim2 + idx2
 *        = (idx%dim1)*dim2 + idx/dim1
 */
static inline int transpose_idx(int idx, int dim1, int dim2)
{
  return idx%dim1*dim2 + idx/dim1;
}

// TODO: only supports local matrix for now
/**
 * Transposition using parallel counting sort
 */
CSR *CSR::transpose() const
{
   const double *A_data = values;
   const int *A_i = rowptr;
   const int *A_j = colidx;
   int num_rowsA = m;
   int num_colsA = n;
   int nnz = rowptr[m];
   int num_nonzerosA = nnz;

   CSR *AT = new CSR();

   AT->m = m;
   AT->n = n;
   AT->colidx = MALLOC(int, nnz);
   assert(AT->colidx);
   if (A_data) {
     AT->values = MALLOC(double, nnz);
     assert(AT->values);
   }
   if (diagptr) {
     AT->diagptr = MALLOC(int, m);
     assert(AT->diagptr);
   }

   if (0 == num_colsA) {
     return AT;
   }

   int *AT_j = AT->colidx;
   double *AT_data = AT->values;

   double t = omp_get_wtime();

   int *bucket = MALLOC(
    int, (num_colsA + 1)*omp_get_max_threads());

#ifndef NDEBUG
  int i;
  for (i = 0; i < num_rowsA; ++i) {
    assert(A_i[i + 1] >= A_i[i]);
  }
#endif

#define MIN(a, b) (((a) <= (b)) ? (a) : (b))

#pragma omp parallel
   {
   int nthreads = omp_get_num_threads();
   int tid = omp_get_thread_num();

   int nnzPerThread = (num_nonzerosA + nthreads - 1)/nthreads;
   int iBegin = lower_bound(A_i, A_i + num_rowsA, nnzPerThread*tid) - A_i;
   int iEnd = lower_bound(A_i, A_i + num_rowsA, nnzPerThread*(tid + 1)) - A_i;

   int i, j;
   memset(bucket + tid*num_colsA, 0, sizeof(int)*num_colsA);

   // count the number of keys that will go into each bucket
   for (j = A_i[iBegin]; j < A_i[iEnd]; ++j) {
     int idx = A_j[j];
#ifndef NDEBUG
     if (idx < 0 || idx >= num_colsA) {
       printf("tid = %d num_rowsA = %d num_colsA = %d num_nonzerosA = %d iBegin = %d iEnd = %d A_i[iBegin] = %d A_i[iEnd] = %d j = %d idx = %d\n", tid, num_rowsA, num_colsA, num_nonzerosA, iBegin, iEnd, A_i[iBegin], A_i[iEnd], j, idx);
     }
#endif
     assert(idx >= 0 && idx < num_colsA);
     bucket[tid*num_colsA + idx]++;
   }
   // up to here, bucket is used as int[nthreads][num_colsA] 2D array

   // prefix sum
#pragma omp barrier

   for (i = tid*num_colsA + 1; i < (tid + 1)*num_colsA; ++i) {
     int transpose_i = transpose_idx(i, nthreads, num_colsA);
     int transpose_i_minus_1 = transpose_idx(i - 1, nthreads, num_colsA);

     bucket[transpose_i] += bucket[transpose_i_minus_1];
   }

#pragma omp barrier
#pragma omp master
   {
     for (i = 1; i < nthreads; ++i) {
       int j0 = num_colsA*i - 1, j1 = num_colsA*(i + 1) - 1;
       int transpose_j0 = transpose_idx(j0, nthreads, num_colsA);
       int transpose_j1 = transpose_idx(j1, nthreads, num_colsA);

       bucket[transpose_j1] += bucket[transpose_j0];
     }
     bucket[num_colsA] = num_nonzerosA;
   }
#pragma omp barrier

   if (tid > 0) {
     int transpose_i0 = transpose_idx(num_colsA*tid - 1, nthreads, num_colsA);

     for (i = tid*num_colsA; i < (tid + 1)*num_colsA - 1; ++i) {
       int transpose_i = transpose_idx(i, nthreads, num_colsA);

       bucket[transpose_i] += bucket[transpose_i0];
     }
   }

#pragma omp barrier

   if (A_data) {
      for (i = iEnd - 1; i >= iBegin; --i) {
        for (j = A_i[i + 1] - 1; j >= A_i[i]; --j) {
          int idx = A_j[j];
          --bucket[tid*num_colsA + idx];

          int offset = bucket[tid*num_colsA + idx];

          assert(offset >= 0 && offset < num_nonzerosA);
          AT_data[offset] = A_data[j];
          AT_j[offset] = i;
        }
      }
   }
   else {
      for (i = iEnd - 1; i >= iBegin; --i) {
        for (j = A_i[i + 1] - 1; j >= A_i[i]; --j) {
          int idx = A_j[j];
          --bucket[tid*num_colsA + idx];

          int offset = bucket[tid*num_colsA + idx];

          AT_j[offset] = i;
        }
      }
   }

   if (diagptr) {
#pragma omp barrier
     for (i = iBegin; i < iEnd; ++i) {
       for (int j = bucket[i]; j < bucket[i + 1]; ++j) {
         int c = AT_j[j];
         if (c == i) AT->diagptr[i] = j;
       }
     }
   }

   } // omp parallel

   AT->rowptr = bucket;

   return AT;
 }

template<int BASE = 0>
int getBandwidth_(const CSR *A)
{
  int bw = INT_MIN;
#pragma omp parallel for reduction(max:bw)
  for (int i = 0; i < A->m; ++i) {
    for (int j = A->rowptr[i] - BASE; j < A->rowptr[i + 1] - BASE; ++j) {
      int c = A->colidx[j] - BASE;
      int temp = c - i;
      if (temp < 0) temp = -temp;
      bw = max(temp, bw);
    }
  }
  return bw;
}

int CSR::getBandwidth() const
{
  if (0 == base) {
    return getBandwidth_<0>(this);
  }
  else {
    assert(1 == base);
    return getBandwidth_<1>(this);
  }
}

} // namespace SpMP
<|endoftext|>


<commit_before><commit_msg>Workaround a race condition caused by not synchronization destruction of the pipeline.<commit_after><|endoftext|>