<commit_before>/*
 * Copyright (c) 2011-2013 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/bitcoin/utility/threadpool.hpp>

#include <new>
#include <system_error>
#include <thread>
#include <bitcoin/bitcoin/utility/thread.hpp>

namespace libbitcoin {

using boost::asio::io_service;

threadpool::threadpool(size_t number_threads, thread_priority priority)
  : work_(nullptr)
{
    spawn(number_threads, priority);
}
threadpool::~threadpool()
{
    shutdown();
}

void threadpool::spawn(size_t number_threads, thread_priority priority)
{
    for (size_t i = 0; i < number_threads; ++i)
        spawn_once(priority);
}
void threadpool::spawn_once(thread_priority priority)
{
    if (work_ == nullptr)
        work_ = new io_service::work(ios_);

    auto action = [this, priority]
    {
        set_thread_priority(priority);
        ios_.run();
    };

    threads_.push_back(std::thread(action));
}

void threadpool::stop()
{
    ios_.stop();
}
void threadpool::shutdown()
{
    if (work_ != nullptr)
        delete work_;

    work_ = nullptr;
}
void threadpool::join()
{
    for (std::thread& thread: threads_)
    {
        try
        {
            thread.join();
        }
        catch (const std::system_error&)
        {
            // other than logging, or altering the signature to
            // return the error to the join caller, not sure what
            // to do here.
        }
    }
}

io_service& threadpool::service()
{
    return ios_;
}
const io_service& threadpool::service() const
{
    return ios_;
}

async_strand::async_strand(threadpool& pool)
  : ios_(pool.service()), strand_(ios_)
{
}

} // namespace libbitcoin

<commit_msg>Try alternate thread.join() patch.<commit_after>/*
 * Copyright (c) 2011-2013 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/bitcoin/utility/threadpool.hpp>

#include <new>
#include <system_error>
#include <thread>
#include <bitcoin/bitcoin/utility/thread.hpp>

namespace libbitcoin {

using boost::asio::io_service;

threadpool::threadpool(size_t number_threads, thread_priority priority)
  : work_(nullptr)
{
    spawn(number_threads, priority);
}
threadpool::~threadpool()
{
    shutdown();
}

void threadpool::spawn(size_t number_threads, thread_priority priority)
{
    for (size_t i = 0; i < number_threads; ++i)
        spawn_once(priority);
}
void threadpool::spawn_once(thread_priority priority)
{
    if (work_ == nullptr)
        work_ = new io_service::work(ios_);

    auto action = [this, priority]
    {
        set_thread_priority(priority);
        ios_.run();
    };

    threads_.push_back(std::thread(action));
}

void threadpool::stop()
{
    ios_.stop();
}
void threadpool::shutdown()
{
    if (work_ != nullptr)
        delete work_;

    work_ = nullptr;
}
void threadpool::join()
{
    for (auto& thread: threads_)
    //{
        //try
        //{
            if (thread.joinable())
                thread.join();
        //}
        //catch (const std::system_error&)
        //{
        //    // other than logging, or altering the signature to
        //    // return the error to the join caller, not sure what
        //    // to do here.
        //}
    //}
}

io_service& threadpool::service()
{
    return ios_;
}
const io_service& threadpool::service() const
{
    return ios_;
}

async_strand::async_strand(threadpool& pool)
  : ios_(pool.service()), strand_(ios_)
{
}

} // namespace libbitcoin

<|endoftext|>


<commit_before>//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include <stack>

#include "linearAlg2D.h"
#include "logoutput.h"
#include "VoronoiUtils.h"
#include "optional.h"

namespace cura
{

Point VoronoiUtils::p(const vd_t::vertex_type* node)
{
    return Point(node->x() + 0, node->y() + 0); // gets rid of negative zero
}

bool VoronoiUtils::isSourcePoint(Point p, const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments, coord_t snap_dist)
{
    if (cell.contains_point())
    {
        return shorterThen(p - getSourcePoint(cell, points, segments), snap_dist);
    }
    else
    {
        const Segment& segment = getSourceSegment(cell, points, segments);
        return shorterThen(p - segment.from(), snap_dist) || shorterThen(p - segment.to(), snap_dist);
    }
}

coord_t VoronoiUtils::getDistance(Point p, const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments)
{
    if (cell.contains_point())
    {
        return vSize(p - getSourcePoint(cell, points, segments));
    }
    else
    {
        const Segment& segment = getSourceSegment(cell, points, segments);
        return sqrt(LinearAlg2D::getDist2FromLineSegment(segment.from(), p, segment.to()));
    }
}

Point VoronoiUtils::getSourcePoint(const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments)
{
    assert(cell.contains_point());
    if(!cell.contains_point())
    {
        logWarning("Voronoi cell doesn't contain a source point!");
    }
    switch (cell.source_category())
    {
        case boost::polygon::SOURCE_CATEGORY_SINGLE_POINT:
            return points[cell.source_index()];
            break;
        case boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT:
            assert(cell.source_index() - points.size() < segments.size());
            return segments[cell.source_index() - points.size()].to();
            break;
        case boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT:
            assert(cell.source_index() - points.size() < segments.size());
            return segments[cell.source_index() - points.size()].from();
            break;
        default:
            assert(false && "getSourcePoint should only be called on point cells!\n");
            break;
    }
    return points[cell.source_index()];
}

PolygonsPointIndex VoronoiUtils::getSourcePointIndex(const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments)
{
    assert(cell.contains_point());
    if(!cell.contains_point())
    {
        logWarning("Voronoi cell doesn't contain a source point!");
    }
    assert(cell.source_category() != boost::polygon::SOURCE_CATEGORY_SINGLE_POINT);
    switch (cell.source_category())
    {
        case boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT:
        {
            assert(cell.source_index() - points.size() < segments.size());
            PolygonsPointIndex ret = segments[cell.source_index() - points.size()];
            ++ret;
            return ret;
            break;
        }
        case boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT:
        {
            assert(cell.source_index() - points.size() < segments.size());
            return segments[cell.source_index() - points.size()];
            break;
        }
        default:
            assert(false && "getSourcePoint should only be called on point cells!\n");
            break;
    }
    PolygonsPointIndex ret = segments[cell.source_index() - points.size()];
    return ++ret;
}

const VoronoiUtils::Segment& VoronoiUtils::getSourceSegment(const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments)
{
    assert(cell.contains_segment());
    if(!cell.contains_segment())
    {
        logWarning("Voronoi cell doesn't contain a source segment!");
    }
    return segments[cell.source_index() - points.size()];
}

std::vector<Point> VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle)
{
    std::vector<Point> discretized;
    // x is distance of point projected on the segment ab
    // xx is point projected on the segment ab
    const Point a = segment.from();
    const Point b = segment.to();
    const Point ab = b - a;
    const Point as = s - a;
    const Point ae = e - a;
    const coord_t ab_size = vSize(ab);
    const coord_t sx = dot(as, ab) / ab_size;
    const coord_t ex = dot(ae, ab) / ab_size;
    const coord_t sxex = ex - sx;

    const Point ap = p - a;
    const coord_t px = dot(ap, ab) / ab_size;

    const Point pxx = LinearAlg2D::getClosestOnLine(p, a, b);
    const Point ppxx = pxx - p;
    const coord_t d = vSize(ppxx);
    const PointMatrix rot = PointMatrix(turn90CCW(ppxx));

    if (d == 0)
    {
        discretized.emplace_back(s);
        discretized.emplace_back(e);
        return discretized;
    }

    const float marking_bound = atan(transitioning_angle * 0.5);
    coord_t msx = - marking_bound * d; // projected marking_start
    coord_t mex = marking_bound * d; // projected marking_end
    const coord_t marking_start_end_h = msx * msx / (2 * d) + d / 2;
    Point marking_start = rot.unapply(Point(msx, marking_start_end_h)) + pxx;
    Point marking_end = rot.unapply(Point(mex, marking_start_end_h)) + pxx;
    const int dir = (sx > ex) ? -1 : 1;
    if (dir < 0)
    {
        std::swap(marking_start, marking_end);
        std::swap(msx, mex);
    }

    bool add_marking_start = msx * dir > (sx - px) * dir && msx * dir < (ex - px) * dir;
    bool add_marking_end = mex * dir > (sx - px) * dir && mex * dir < (ex - px) * dir;

    const Point apex = rot.unapply(Point(0, d / 2)) + pxx;
    bool add_apex = (sx - px) * dir < 0 && (ex - px) * dir > 0;

    assert(!(add_marking_start && add_marking_end) || add_apex);
    if(add_marking_start && add_marking_end && !add_apex)
    {
        logWarning("Failing to discretize parabola! Must add an apex or one of the endpoints.");
    }

    const coord_t step_count = static_cast<coord_t>(static_cast<float>(std::abs(ex - sx)) / approximate_step_size + 0.5);

    discretized.emplace_back(s);
    for (coord_t step = 1; step < step_count; step++)
    {
        const coord_t x = sx + sxex * step / step_count - px;
        const coord_t y = x * x / (2 * d) + d / 2;

        if (add_marking_start && msx * dir < x * dir)
        {
            discretized.emplace_back(marking_start);
            add_marking_start = false;
        }
        if (add_apex && x * dir > 0)
        {
            discretized.emplace_back(apex);
            add_apex = false; // only add the apex just before the
        }
        if (add_marking_end && mex * dir < x * dir)
        {
            discretized.emplace_back(marking_end);
            add_marking_end = false;
        }
        const Point result = rot.unapply(Point(x, y)) + pxx;
        discretized.emplace_back(result);
    }
    if (add_apex)
    {
        discretized.emplace_back(apex);
    }
    if (add_marking_end)
    {
        discretized.emplace_back(marking_end);
    }
    discretized.emplace_back(e);
    return discretized;
}

/*
The following code has been adjusted from an implementation of Boost. This is the license:

Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

// adapted from boost::polygon::voronoi_visual_utils.cpp
void VoronoiUtils::discretize(const Point& point, const Segment& segment, const coord_t max_dist, std::vector<Point>* discretization)
{
    // Apply the linear transformation to move start point of the segment to
    // the point with coordinates (0, 0) and the direction of the segment to
    // coincide the positive direction of the x-axis.
    const Point segm_vec = segment.to() - segment.from();
    const coord_t segment_length2 = vSize2(segm_vec);

    // Compute x-coordinates of the endpoints of the edge
    // in the transformed space.
    const coord_t projection_start = segment_length2 * getPointProjection((*discretization)[0], segment);
    const coord_t projection_end = segment_length2 * getPointProjection((*discretization)[1], segment);

    // Compute parabola parameters in the transformed space.
    // Parabola has next representation:
    // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y).
    const Point point_vec = point - segment.from();
    const coord_t rot_x = dot(segm_vec, point_vec);
    const coord_t rot_y = cross(segm_vec, point_vec);

    // Save the last point.
    const Point last_point = (*discretization)[1];
    discretization->pop_back();

    // Use stack to avoid recursion.
    std::stack<coord_t> point_stack;
    point_stack.push(projection_end);
    Point cur(projection_start, parabolaY(projection_start, rot_x, rot_y));

    // Adjust max_dist parameter in the transformed space.
    const coord_t max_dist_transformed = max_dist * max_dist * segment_length2;
    while (!point_stack.empty())
    {
        const Point new_(point_stack.top(), parabolaY(point_stack.top(), rot_x, rot_y));
        const Point new_vec = new_ - cur;

        // Compute coordinates of the point of the parabola that is
        // furthest from the current line segment.
        const coord_t mid_x = new_vec.Y * rot_y / new_vec.X + rot_x;
        const coord_t mid_y = parabolaY(mid_x, rot_x, rot_y);
        Point mid_vec = Point(mid_x, mid_y) - cur;

        // Compute maximum distance between the given parabolic arc
        // and line segment that discretize it.
        coord_t dist = cross(mid_vec, new_vec);
        dist = dist * dist / vSize2(new_vec); // TODO overflows!!!
        if (dist <= max_dist_transformed)
        {
            // Distance between parabola and line segment is less than max_dist.
            point_stack.pop();
            const coord_t inter_x = (segm_vec.X * new_.X - segm_vec.Y * new_.Y) / segment_length2 + segment.from().X;
            const coord_t inter_y = (segm_vec.X * new_.Y + segm_vec.Y * new_.X) / segment_length2 + segment.from().Y;
            discretization->push_back(Point(inter_x, inter_y));
            cur = new_;
        }
        else
        {
            point_stack.push(mid_x);
        }
    }

    // Update last point.
    discretization->back() = last_point;
}

// adapted from boost::polygon::voronoi_visual_utils.cpp
coord_t VoronoiUtils::parabolaY(coord_t x, coord_t a, coord_t b)
{
    return ((x - a) * (x - a) + b * b) / (b + b);
}

// adapted from boost::polygon::voronoi_visual_utils.cpp
double VoronoiUtils::getPointProjection(const Point& point, const Segment& segment)
{
    Point segment_vec = segment.to() - segment.from();
    Point point_vec = point - segment.from();
    coord_t sqr_segment_length = vSize2(segment_vec);
    coord_t vec_dot = dot(segment_vec, point_vec);
    return static_cast<double>(vec_dot) / sqr_segment_length;
}

}//namespace cura
<commit_msg>Properly round Voronoi coordinates to IntPoint coordinates<commit_after>//Copyright (c) 2021 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include <stack>

#include "linearAlg2D.h"
#include "logoutput.h"
#include "VoronoiUtils.h"
#include "optional.h"

namespace cura
{

Point VoronoiUtils::p(const vd_t::vertex_type* node)
{
    return Point(node->x() + 0.5d, node->y() + 0.5d); //Round to nearest integer coordinates.
}

bool VoronoiUtils::isSourcePoint(Point p, const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments, coord_t snap_dist)
{
    if (cell.contains_point())
    {
        return shorterThen(p - getSourcePoint(cell, points, segments), snap_dist);
    }
    else
    {
        const Segment& segment = getSourceSegment(cell, points, segments);
        return shorterThen(p - segment.from(), snap_dist) || shorterThen(p - segment.to(), snap_dist);
    }
}

coord_t VoronoiUtils::getDistance(Point p, const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments)
{
    if (cell.contains_point())
    {
        return vSize(p - getSourcePoint(cell, points, segments));
    }
    else
    {
        const Segment& segment = getSourceSegment(cell, points, segments);
        return sqrt(LinearAlg2D::getDist2FromLineSegment(segment.from(), p, segment.to()));
    }
}

Point VoronoiUtils::getSourcePoint(const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments)
{
    assert(cell.contains_point());
    if(!cell.contains_point())
    {
        logWarning("Voronoi cell doesn't contain a source point!");
    }
    switch (cell.source_category())
    {
        case boost::polygon::SOURCE_CATEGORY_SINGLE_POINT:
            return points[cell.source_index()];
            break;
        case boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT:
            assert(cell.source_index() - points.size() < segments.size());
            return segments[cell.source_index() - points.size()].to();
            break;
        case boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT:
            assert(cell.source_index() - points.size() < segments.size());
            return segments[cell.source_index() - points.size()].from();
            break;
        default:
            assert(false && "getSourcePoint should only be called on point cells!\n");
            break;
    }
    return points[cell.source_index()];
}

PolygonsPointIndex VoronoiUtils::getSourcePointIndex(const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments)
{
    assert(cell.contains_point());
    if(!cell.contains_point())
    {
        logWarning("Voronoi cell doesn't contain a source point!");
    }
    assert(cell.source_category() != boost::polygon::SOURCE_CATEGORY_SINGLE_POINT);
    switch (cell.source_category())
    {
        case boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT:
        {
            assert(cell.source_index() - points.size() < segments.size());
            PolygonsPointIndex ret = segments[cell.source_index() - points.size()];
            ++ret;
            return ret;
            break;
        }
        case boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT:
        {
            assert(cell.source_index() - points.size() < segments.size());
            return segments[cell.source_index() - points.size()];
            break;
        }
        default:
            assert(false && "getSourcePoint should only be called on point cells!\n");
            break;
    }
    PolygonsPointIndex ret = segments[cell.source_index() - points.size()];
    return ++ret;
}

const VoronoiUtils::Segment& VoronoiUtils::getSourceSegment(const vd_t::cell_type& cell, const std::vector<Point>& points, const std::vector<Segment>& segments)
{
    assert(cell.contains_segment());
    if(!cell.contains_segment())
    {
        logWarning("Voronoi cell doesn't contain a source segment!");
    }
    return segments[cell.source_index() - points.size()];
}

std::vector<Point> VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle)
{
    std::vector<Point> discretized;
    // x is distance of point projected on the segment ab
    // xx is point projected on the segment ab
    const Point a = segment.from();
    const Point b = segment.to();
    const Point ab = b - a;
    const Point as = s - a;
    const Point ae = e - a;
    const coord_t ab_size = vSize(ab);
    const coord_t sx = dot(as, ab) / ab_size;
    const coord_t ex = dot(ae, ab) / ab_size;
    const coord_t sxex = ex - sx;

    const Point ap = p - a;
    const coord_t px = dot(ap, ab) / ab_size;

    const Point pxx = LinearAlg2D::getClosestOnLine(p, a, b);
    const Point ppxx = pxx - p;
    const coord_t d = vSize(ppxx);
    const PointMatrix rot = PointMatrix(turn90CCW(ppxx));

    if (d == 0)
    {
        discretized.emplace_back(s);
        discretized.emplace_back(e);
        return discretized;
    }

    const float marking_bound = atan(transitioning_angle * 0.5);
    coord_t msx = - marking_bound * d; // projected marking_start
    coord_t mex = marking_bound * d; // projected marking_end
    const coord_t marking_start_end_h = msx * msx / (2 * d) + d / 2;
    Point marking_start = rot.unapply(Point(msx, marking_start_end_h)) + pxx;
    Point marking_end = rot.unapply(Point(mex, marking_start_end_h)) + pxx;
    const int dir = (sx > ex) ? -1 : 1;
    if (dir < 0)
    {
        std::swap(marking_start, marking_end);
        std::swap(msx, mex);
    }

    bool add_marking_start = msx * dir > (sx - px) * dir && msx * dir < (ex - px) * dir;
    bool add_marking_end = mex * dir > (sx - px) * dir && mex * dir < (ex - px) * dir;

    const Point apex = rot.unapply(Point(0, d / 2)) + pxx;
    bool add_apex = (sx - px) * dir < 0 && (ex - px) * dir > 0;

    assert(!(add_marking_start && add_marking_end) || add_apex);
    if(add_marking_start && add_marking_end && !add_apex)
    {
        logWarning("Failing to discretize parabola! Must add an apex or one of the endpoints.");
    }

    const coord_t step_count = static_cast<coord_t>(static_cast<float>(std::abs(ex - sx)) / approximate_step_size + 0.5);

    discretized.emplace_back(s);
    for (coord_t step = 1; step < step_count; step++)
    {
        const coord_t x = sx + sxex * step / step_count - px;
        const coord_t y = x * x / (2 * d) + d / 2;

        if (add_marking_start && msx * dir < x * dir)
        {
            discretized.emplace_back(marking_start);
            add_marking_start = false;
        }
        if (add_apex && x * dir > 0)
        {
            discretized.emplace_back(apex);
            add_apex = false; // only add the apex just before the
        }
        if (add_marking_end && mex * dir < x * dir)
        {
            discretized.emplace_back(marking_end);
            add_marking_end = false;
        }
        const Point result = rot.unapply(Point(x, y)) + pxx;
        discretized.emplace_back(result);
    }
    if (add_apex)
    {
        discretized.emplace_back(apex);
    }
    if (add_marking_end)
    {
        discretized.emplace_back(marking_end);
    }
    discretized.emplace_back(e);
    return discretized;
}

/*
The following code has been adjusted from an implementation of Boost. This is the license:

Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

// adapted from boost::polygon::voronoi_visual_utils.cpp
void VoronoiUtils::discretize(const Point& point, const Segment& segment, const coord_t max_dist, std::vector<Point>* discretization)
{
    // Apply the linear transformation to move start point of the segment to
    // the point with coordinates (0, 0) and the direction of the segment to
    // coincide the positive direction of the x-axis.
    const Point segm_vec = segment.to() - segment.from();
    const coord_t segment_length2 = vSize2(segm_vec);

    // Compute x-coordinates of the endpoints of the edge
    // in the transformed space.
    const coord_t projection_start = segment_length2 * getPointProjection((*discretization)[0], segment);
    const coord_t projection_end = segment_length2 * getPointProjection((*discretization)[1], segment);

    // Compute parabola parameters in the transformed space.
    // Parabola has next representation:
    // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y).
    const Point point_vec = point - segment.from();
    const coord_t rot_x = dot(segm_vec, point_vec);
    const coord_t rot_y = cross(segm_vec, point_vec);

    // Save the last point.
    const Point last_point = (*discretization)[1];
    discretization->pop_back();

    // Use stack to avoid recursion.
    std::stack<coord_t> point_stack;
    point_stack.push(projection_end);
    Point cur(projection_start, parabolaY(projection_start, rot_x, rot_y));

    // Adjust max_dist parameter in the transformed space.
    const coord_t max_dist_transformed = max_dist * max_dist * segment_length2;
    while (!point_stack.empty())
    {
        const Point new_(point_stack.top(), parabolaY(point_stack.top(), rot_x, rot_y));
        const Point new_vec = new_ - cur;

        // Compute coordinates of the point of the parabola that is
        // furthest from the current line segment.
        const coord_t mid_x = new_vec.Y * rot_y / new_vec.X + rot_x;
        const coord_t mid_y = parabolaY(mid_x, rot_x, rot_y);
        Point mid_vec = Point(mid_x, mid_y) - cur;

        // Compute maximum distance between the given parabolic arc
        // and line segment that discretize it.
        coord_t dist = cross(mid_vec, new_vec);
        dist = dist * dist / vSize2(new_vec); // TODO overflows!!!
        if (dist <= max_dist_transformed)
        {
            // Distance between parabola and line segment is less than max_dist.
            point_stack.pop();
            const coord_t inter_x = (segm_vec.X * new_.X - segm_vec.Y * new_.Y) / segment_length2 + segment.from().X;
            const coord_t inter_y = (segm_vec.X * new_.Y + segm_vec.Y * new_.X) / segment_length2 + segment.from().Y;
            discretization->push_back(Point(inter_x, inter_y));
            cur = new_;
        }
        else
        {
            point_stack.push(mid_x);
        }
    }

    // Update last point.
    discretization->back() = last_point;
}

// adapted from boost::polygon::voronoi_visual_utils.cpp
coord_t VoronoiUtils::parabolaY(coord_t x, coord_t a, coord_t b)
{
    return ((x - a) * (x - a) + b * b) / (b + b);
}

// adapted from boost::polygon::voronoi_visual_utils.cpp
double VoronoiUtils::getPointProjection(const Point& point, const Segment& segment)
{
    Point segment_vec = segment.to() - segment.from();
    Point point_vec = point - segment.from();
    coord_t sqr_segment_length = vSize2(segment_vec);
    coord_t vec_dot = dot(segment_vec, point_vec);
    return static_cast<double>(vec_dot) / sqr_segment_length;
}

}//namespace cura
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2015 Marius Kaufmann, Tamara Frieß, Jannis Hoppe, Christian Hack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/fft_strategy.h"

namespace taylortrack {
namespace utils {
void FftStrategy::circshift(const RArray &in,
                            int64_t xdim,
                            int64_t ydim,
                            int64_t xshift,
                            int64_t yshift,
                            RArray &out) {
  for (int64_t i = 0; i < xdim; i++) {
    int64_t ii = (i + xshift) % xdim;
    for (int64_t j = 0; j < ydim; j++) {
      int64_t jj = (j + yshift) % ydim;
      out[ii * ydim + jj] = in[i * ydim + j];
    }
  }
}

CArray FftStrategy::convertToComplex(const RArray &signal) {
  CArray converted(signal.size());
  for (int i = 0; i < signal.size(); i++) {
    converted[i] = signal[i];
  }
  return converted;
}

RArray FftStrategy::convertToReal(const CArray &signal) {
  RArray converted(signal.size());
  for (int i = 0; i < signal.size(); i++) {
    converted[i] = signal[i].real();
  }
  return converted;
}

CArray FftStrategy::zeroPadding(const CArray &signal, int padamount) {
  size_t newsize = padamount + signal.size();
  CArray paddedsignal(newsize);
  // copying the signal
  for (size_t i = 0; i < signal.size(); i++) {
    paddedsignal[i] = signal[i];
  }
  // padding the original signal with the specified amount of zeros.
  for (size_t i = signal.size(); i < newsize; i++) {
    paddedsignal[i] = 0;
  }
  return paddedsignal;
}
}  // namespace utils
}  // namespace taylortrack

<commit_msg>Fixed Warnings<commit_after>/*
The MIT License (MIT)

Copyright (c) 2015 Marius Kaufmann, Tamara Frieß, Jannis Hoppe, Christian Hack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "utils/fft_strategy.h"

namespace taylortrack {
namespace utils {
void FftStrategy::circshift(const RArray &in,
                            int64_t xdim,
                            int64_t ydim,
                            int64_t xshift,
                            int64_t yshift,
                            RArray &out) {
  for (int64_t i = 0; i < xdim; i++) {
    int64_t ii = (i + xshift) % xdim;
    for (int64_t j = 0; j < ydim; j++) {
      int64_t jj = (j + yshift) % ydim;
      out[ii * ydim + jj] = in[i * ydim + j];
    }
  }
}

CArray FftStrategy::convertToComplex(const RArray &signal) {
  CArray converted(signal.size());
  for (int i = 0; i < static_cast<int>(signal.size()); i++) {
    converted[i] = signal[i];
  }
  return converted;
}

RArray FftStrategy::convertToReal(const CArray &signal) {
  RArray converted(signal.size());
  for (int i = 0; i < static_cast<int>(signal.size()); i++) {
    converted[i] = signal[i].real();
  }
  return converted;
}

CArray FftStrategy::zeroPadding(const CArray &signal, int padamount) {
  size_t newsize = padamount + signal.size();
  CArray paddedsignal(newsize);
  // copying the signal
  for (size_t i = 0; i < signal.size(); i++) {
    paddedsignal[i] = signal[i];
  }
  // padding the original signal with the specified amount of zeros.
  for (size_t i = signal.size(); i < newsize; i++) {
    paddedsignal[i] = 0;
  }
  return paddedsignal;
}
}  // namespace utils
}  // namespace taylortrack

<|endoftext|>


<commit_before>#include "nodes/EANode/systems/ES.hpp"
#include "nodes/EANode/selections/RandomSelection.hpp"

ES::ES(
	MutationOperation* mutation
) : EvolutionarySystem(new RandomSelection(), NULL, mutation) {}

ES::ES(
	CrossoverOperation* cross,
	MutationOperation* mutation
) : EvolutionarySystem(new RandomSelection(), cross, mutation) {}

ES::ES(
	SelectionStrategy* strategy,
	CrossoverOperation* cross,
	MutationOperation* mutation
) : EvolutionarySystem(strategy, cross, mutation) {}

std::vector<Genome*> ES::breedMutateSelect(
	std::vector<Genome*> initialPopulation,
	std::vector<float>& populationFitnesses,
	std::vector<ObjectiveFunction*> objectives,
	std::string speciesNode
) {
	std::vector<Genome*> offspring = this->getOffspring(
		initialPopulation,
		populationFitnesses,
		speciesNode
	);
	std::vector<Genome*> results = this->doReplacement(
		offspring,
		initialPopulation,
		populationFitnesses,
		objectives
	);
	std::vector<Genome*> finalResults;
	for (auto genome: results) finalResults.push_back(new Genome(genome));
	for (auto off: offspring) delete(off);
	return finalResults;
}
<commit_msg>[ES]: Fixed broken replacement<commit_after>#include "nodes/EANode/systems/ES.hpp"
#include "nodes/EANode/selections/RandomSelection.hpp"

ES::ES(
	MutationOperation* mutation
) : EvolutionarySystem(new RandomSelection(), NULL, mutation) {}

ES::ES(
	CrossoverOperation* cross,
	MutationOperation* mutation
) : EvolutionarySystem(new RandomSelection(), cross, mutation) {}

ES::ES(
	SelectionStrategy* strategy,
	CrossoverOperation* cross,
	MutationOperation* mutation
) : EvolutionarySystem(strategy, cross, mutation) {}

std::vector<Genome*> ES::breedMutateSelect(
	std::vector<Genome*> initialPopulation,
	std::vector<float>& populationFitnesses,
	std::vector<ObjectiveFunction*> objectives,
	std::string speciesNode
) {
	std::vector<Genome*> offspring = this->getOffspring(
		initialPopulation,
		populationFitnesses,
		speciesNode
	);
	std::vector<Genome*> results = this->doReplacement(
		initialPopulation,
		offspring,
		populationFitnesses,
		objectives
	);
	std::vector<Genome*> finalResults;
	for (auto genome: results) finalResults.push_back(new Genome(genome));
	for (auto off: offspring) delete(off);
	return finalResults;
}
<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <cstdio>

#include <exception>
#include <iostream>
#include <ostream>

#include <visionaray/detail/platform.h>

#ifdef VSNRAY_OS_WIN32
#include <windows.h>
#endif

#include <GL/glew.h>

#include <visionaray/gl/util.h>
#include <visionaray/math/math.h>

#include "../util.h"

namespace gl = visionaray::gl;

#if defined(GL_KHR_debug)

static char const* get_debug_type_string(GLenum type)
{
    switch (type)
    {
    case GL_DEBUG_TYPE_ERROR:
        return "error";
    case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
        return "deprecated behavior detected";
    case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
        return "undefined behavior detected";
    case GL_DEBUG_TYPE_PORTABILITY:
        return "portablility warning";
    case GL_DEBUG_TYPE_PERFORMANCE:
        return "performance warning";
    case GL_DEBUG_TYPE_OTHER:
        return "other";
    case GL_DEBUG_TYPE_MARKER:
        return "marker";
    }

    return "{unknown type}";
}

static void debug_callback(
        GLenum          /*source*/,
        GLenum          type,
        GLuint          /*id*/,
        GLenum          /*severity*/,
        GLsizei         /*length*/,
        const GLchar*   message,
        GLvoid*         /*userParam*/
        )
{
    std::cerr << "GL " << get_debug_type_string(type) << " " << message << std::endl;
    std::cerr << visionaray::util::backtrace() << std::endl;
    throw std::exception();

    if (type == GL_DEBUG_TYPE_ERROR)
    {
#ifdef _WIN32
        if (IsDebuggerPresent())
            DebugBreak();
#endif
    }
}

#endif // GL_KHR_debug

namespace visionaray
{

void gl::init_debug_callback()
{
#if defined(GL_KHR_debug)
    if (GLEW_KHR_debug)
    {
        glEnable(GL_DEBUG_OUTPUT);
        glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);

        glDebugMessageCallback((GLDEBUGPROC)debug_callback, 0);
    }
#elif defined(GL_ARB_debug_output)
    if (GLEW_ARB_debug_output)
    {
    }
#endif
}

std::string gl::last_error()
{
    GLenum err = glGetError();
    if (err != GL_NO_ERROR)
    {
        return std::string(reinterpret_cast<char const*>(glewGetErrorString(err)));
    }
    return "";
}

void gl::draw_full_screen_quad()
{
    glPushAttrib(GL_TEXTURE_BIT | GL_TRANSFORM_BIT);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    glActiveTexture(GL_TEXTURE0);

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0f, -1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0f,  1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f,  1.0f);
    glEnd();

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    glPopAttrib();
}

void gl::blend_texture(GLuint texture, GLenum sfactor, GLenum dfactor)
{
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    glActiveTexture(GL_TEXTURE0);

    glEnable(GL_BLEND);
    glBlendFunc(sfactor, dfactor);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture);

    glDepthMask(GL_FALSE);
    glDisable(GL_LIGHTING);

    draw_full_screen_quad();

    glPopAttrib();
}

void gl::blend_pixels(GLsizei w, GLsizei h, GLenum format, GLenum type, GLvoid const* pixels, GLenum sfactor, GLenum dfactor)
{
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    recti vp = gl::viewport();
    glWindowPos2i(vp[0], vp[1]);

    GLfloat scalex = vp[2] / static_cast<GLfloat>(w);
    GLfloat scaley = vp[3] / static_cast<GLfloat>(h);

    glPixelZoom(scalex, scaley);

    glEnable(GL_BLEND);
    glBlendFunc(sfactor, dfactor);

    glDrawPixels(w, h, format, type, pixels);

    glPopAttrib();
}

recti gl::viewport()
{
    GLint vp[4];
    glGetIntegerv(GL_VIEWPORT, &vp[0]);
    return recti(vp[0], vp[1], vp[2], vp[3]);
}

} // visionaray
<commit_msg>Don't generally exit on debug callback<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <cstdio>

#include <iostream>
#include <ostream>
#include <stdexcept>

#include <visionaray/detail/platform.h>

#ifdef VSNRAY_OS_WIN32
#include <windows.h>
#endif

#include <GL/glew.h>

#include <visionaray/gl/util.h>
#include <visionaray/math/math.h>

#include "../util.h"

namespace gl = visionaray::gl;

#if defined(GL_KHR_debug)

static char const* get_debug_type_string(GLenum type)
{
    switch (type)
    {
    case GL_DEBUG_TYPE_ERROR:
        return "error";
    case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
        return "deprecated behavior detected";
    case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
        return "undefined behavior detected";
    case GL_DEBUG_TYPE_PORTABILITY:
        return "portablility warning";
    case GL_DEBUG_TYPE_PERFORMANCE:
        return "performance warning";
    case GL_DEBUG_TYPE_OTHER:
        return "other";
    case GL_DEBUG_TYPE_MARKER:
        return "marker";
    }

    return "{unknown type}";
}

static void debug_callback(
        GLenum          /*source*/,
        GLenum          type,
        GLuint          /*id*/,
        GLenum          /*severity*/,
        GLsizei         /*length*/,
        const GLchar*   message,
        GLvoid*         /*userParam*/
        )
{
    std::cerr << "GL " << get_debug_type_string(type) << " " << message << std::endl;
    if (type == GL_DEBUG_TYPE_ERROR)
    {
#ifdef _WIN32
        if (IsDebuggerPresent())
        {
            DebugBreak();
        }
#else
        std::cerr << visionaray::util::backtrace() << std::endl;
        throw std::runtime_error("OpenGL error");
#endif
    }
}

#endif // GL_KHR_debug

namespace visionaray
{

void gl::init_debug_callback()
{
#if defined(GL_KHR_debug)
    if (GLEW_KHR_debug)
    {
        glEnable(GL_DEBUG_OUTPUT);
        glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);

        glDebugMessageCallback((GLDEBUGPROC)debug_callback, 0);
    }
#elif defined(GL_ARB_debug_output)
    if (GLEW_ARB_debug_output)
    {
    }
#endif
}

std::string gl::last_error()
{
    GLenum err = glGetError();
    if (err != GL_NO_ERROR)
    {
        return std::string(reinterpret_cast<char const*>(glewGetErrorString(err)));
    }
    return "";
}

void gl::draw_full_screen_quad()
{
    glPushAttrib(GL_TEXTURE_BIT | GL_TRANSFORM_BIT);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    glActiveTexture(GL_TEXTURE0);

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0f, -1.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0f,  1.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f,  1.0f);
    glEnd();

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    glPopAttrib();
}

void gl::blend_texture(GLuint texture, GLenum sfactor, GLenum dfactor)
{
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    glActiveTexture(GL_TEXTURE0);

    glEnable(GL_BLEND);
    glBlendFunc(sfactor, dfactor);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture);

    glDepthMask(GL_FALSE);
    glDisable(GL_LIGHTING);

    draw_full_screen_quad();

    glPopAttrib();
}

void gl::blend_pixels(GLsizei w, GLsizei h, GLenum format, GLenum type, GLvoid const* pixels, GLenum sfactor, GLenum dfactor)
{
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    recti vp = gl::viewport();
    glWindowPos2i(vp[0], vp[1]);

    GLfloat scalex = vp[2] / static_cast<GLfloat>(w);
    GLfloat scaley = vp[3] / static_cast<GLfloat>(h);

    glPixelZoom(scalex, scaley);

    glEnable(GL_BLEND);
    glBlendFunc(sfactor, dfactor);

    glDrawPixels(w, h, format, type, pixels);

    glPopAttrib();
}

recti gl::viewport()
{
    GLint vp[4];
    glGetIntegerv(GL_VIEWPORT, &vp[0]);
    return recti(vp[0], vp[1], vp[2], vp[3]);
}

} // visionaray
<|endoftext|>


<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/c/eager/gradient_checker.h"

#include <memory>

#include "absl/types/span.h"
#include "tensorflow/c/eager/abstract_tensor_handle.h"
#include "tensorflow/c/eager/c_api_experimental.h"
#include "tensorflow/c/eager/c_api_unified_experimental.h"
#include "tensorflow/c/eager/c_api_unified_experimental_internal.h"
#include "tensorflow/c/eager/gradients.h"
#include "tensorflow/c/eager/gradients_internal.h"
#include "tensorflow/c/eager/gradients_util.h"
#include "tensorflow/c/eager/mnist_gradients_testutil.h"
#include "tensorflow/c/experimental/gradients/math_grad.h"
#include "tensorflow/c/experimental/gradients/nn_grad.h"
#include "tensorflow/c/experimental/ops/array_ops.h"
#include "tensorflow/c/tf_status_helper.h"
#include "tensorflow/c/tf_tensor.h"
#include "tensorflow/core/lib/llvm_rtti/llvm_rtti.h"
#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/test.h"

namespace tensorflow {
namespace gradients {
namespace internal {
namespace {

class GradientCheckerTest
    : public ::testing::TestWithParam<std::tuple<const char*, bool, bool>> {
 protected:
  void SetUp() override {
    TF_StatusPtr status(TF_NewStatus());
    TF_SetTracingImplementation(std::get<0>(GetParam()), status.get());
    Status s = StatusFromTF_Status(status.get());
    CHECK_EQ(errors::OK, s.code()) << s.error_message();
  }
};

Status RegisterGradients(GradientRegistry* registry) {
  TF_RETURN_IF_ERROR(registry->Register("MatMul", MatMulRegisterer));
  TF_RETURN_IF_ERROR(
      registry->Register("SparseSoftmaxCrossEntropyWithLogits",
                         SparseSoftmaxCrossEntropyLossRegisterer));
  return Status::OK();
}

TEST_P(GradientCheckerTest, TestGradCheckMatMul) {
  std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
      TF_NewStatus(), TF_DeleteStatus);
  AbstractContextPtr ctx;
  {
    AbstractContext* ctx_raw = nullptr;
    Status s =
        BuildImmediateExecutionContext(std::get<1>(GetParam()), &ctx_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    ctx.reset(ctx_raw);
  }

  float A_vals[] = {1.0f, 2.0f, 3.0f, 4.0f};
  int64_t A_dims[] = {2, 2};
  float B_vals[] = {.5f, -1.0f, 1.0f, 1.0f};
  int64_t B_dims[] = {2, 2};
  int num_dims = 2;

  AbstractTensorHandlePtr A =
      GetTensorHandleUtilFloat(ctx.get(), A_vals, A_dims, num_dims);
  AbstractTensorHandlePtr B =
      GetTensorHandleUtilFloat(ctx.get(), B_vals, B_dims, num_dims);

  std::vector<AbstractTensorHandle*> inputs;
  inputs.push_back(A.get());
  inputs.push_back(B.get());

  AbstractTensorHandle* grad_approx;
  Status s = CalcNumericalGrad(
      ctx.get(), MatMulModel, absl::MakeSpan(inputs), /*input_index=*/0,
      /*use_function=*/!std::get<2>(GetParam()), &grad_approx);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  TF_Tensor* gt;
  GetValue(grad_approx, &gt);
  float result_data[4] = {0};
  memcpy(&result_data[0], TF_TensorData(gt), TF_TensorByteSize(gt));

  float expected_dA[4] = {-.5f, 2.0f, -.5f, 2.0f};
  float tolerance = 1e-2;
  for (int j = 0; j < 4; j++) {
    ASSERT_NEAR(expected_dA[j], result_data[j], tolerance);
  }
  TF_DeleteTensor(gt);
}

TEST_P(GradientCheckerTest, TestGradCheckMul) {
  std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
      TF_NewStatus(), TF_DeleteStatus);

  AbstractContextPtr ctx;
  {
    AbstractContext* ctx_raw = nullptr;
    Status s =
        BuildImmediateExecutionContext(std::get<1>(GetParam()), &ctx_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    ctx.reset(ctx_raw);
  }

  AbstractTensorHandlePtr x;
  {
    AbstractTensorHandle* x_raw = nullptr;
    Status s = ScalarTensorHandle(ctx.get(), 2.0f, &x_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    x.reset(x_raw);
  }

  AbstractTensorHandlePtr y;
  {
    AbstractTensorHandle* y_raw = nullptr;
    Status s = ScalarTensorHandle(ctx.get(), 7.0f, &y_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    y.reset(y_raw);
  }

  // Will perform z = x*y.
  // dz/dx = y

  std::vector<AbstractTensorHandle*> inputs;
  inputs.push_back(x.get());
  inputs.push_back(y.get());
  float dapprox[1] = {0};
  AbstractTensorHandle* g;

  Status s = CalcNumericalGrad(ctx.get(), MulModel, absl::MakeSpan(inputs),
                               /*input_index=*/0,
                               /*use_function=*/!std::get<2>(GetParam()), &g);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  TF_Tensor* gt;
  GetValue(g, &gt);
  float result_data[1] = {0};
  memcpy(&result_data[0], TF_TensorData(gt), TF_TensorByteSize(gt));

  ASSERT_NEAR(result_data[0], 7.0f, /*tolerance=*/1e-3);
  TF_DeleteTensor(gt);
}

TEST_P(GradientCheckerTest, TestGradCheckSoftmax) {
  std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
      TF_NewStatus(), TF_DeleteStatus);

  /** Test to show how to use this API with analytical gradients:
   *
   *  We have `SoftmaxLossGradModel`, which is a wrapper for the
   *  Softmax analytical gradient found in c/experimental/nn_grads.
   *
   *  We will use the GradientChecker by applying finite differences
   *  to the forward pass wrapped in `SoftmaxModel` and verify that
   *  both the analytical and numerical gradients are relatively
   *  close.
   *
   */

  AbstractContextPtr ctx;
  {
    AbstractContext* ctx_raw = nullptr;
    Status s =
        BuildImmediateExecutionContext(std::get<1>(GetParam()), &ctx_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    ctx.reset(ctx_raw);
  }

  // X = scores
  float X_vals[] = {1.0f, 2.0f, 3.0f, -5.0f, -4.0f, -3.0f, 2.0f, 0.0f, 1.0f};
  int64_t X_dims[] = {3, 3};
  int num_dims = 2;
  AbstractTensorHandlePtr X =
      GetTensorHandleUtilFloat(ctx.get(), X_vals, X_dims, num_dims);

  // y = labels
  int y_vals[] = {1, 0, 1};
  int64_t y_dims[] = {3};
  num_dims = sizeof(y_dims) / sizeof(y_dims[0]);
  AbstractTensorHandlePtr y =
      GetTensorHandleUtilInt(ctx.get(), y_vals, y_dims, num_dims);

  GradientRegistry registry;
  Status s = RegisterGradients(&registry);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  std::vector<AbstractTensorHandle*> inputs;
  inputs.push_back(X.get());
  inputs.push_back(y.get());

  // Run analytical gradient and get its data.
  std::vector<AbstractTensorHandle*> outputs(2);
  s = RunModel(SoftmaxLossGradModel, ctx.get(), absl::MakeSpan(inputs),
               absl::MakeSpan(outputs),
               /*use_function=*/!std::get<2>(GetParam()), registry);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  TF_Tensor* dX_tensor;
  s = GetValue(outputs[0], &dX_tensor);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  float danalytical[9] = {0};  // Contains data from analytical gradient.
  memcpy(&danalytical[0], TF_TensorData(dX_tensor),
         TF_TensorByteSize(dX_tensor));

  // Run numerical gradient approximation using the GradientChecker API.
  AbstractTensorHandle* g;  // Will contain numerical approximation data.
  s = CalcNumericalGrad(ctx.get(), SoftmaxModel, absl::MakeSpan(inputs),
                        /*input_index=*/0,
                        /*use_function=*/!std::get<2>(GetParam()), &g);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  TF_Tensor* gt;
  GetValue(g, &gt);
  float dnumerical[9] = {0};
  memcpy(&dnumerical[0], TF_TensorData(gt), TF_TensorByteSize(gt));

  // Now compare the two implementations:
  for (int j = 0; j < 9; j++) {
    ASSERT_NEAR(dnumerical[j], danalytical[j], /*tolerance=*/1e-3);
  }

  // Only Unref() first output as 2nd is nullptr grad for labels
  outputs[0]->Unref();
  TF_DeleteTensor(dX_tensor);
  TF_DeleteTensor(gt);
}

#ifdef PLATFORM_GOOGLE
INSTANTIATE_TEST_SUITE_P(
    UnifiedCAPI, GradientCheckerTest,
    ::testing::Combine(::testing::Values("graphdef"),
                       /*tfrt*/ ::testing::Values(false),
                       /*executing_eagerly*/ ::testing::Values(true, false)));
#else
INSTANTIATE_TEST_SUITE_P(
    UnifiedCAPI, GradientCheckerTest,
    ::testing::Combine(::testing::Values("graphdef"),
                       /*tfrt*/ ::testing::Values(false),
                       /*executing_eagerly*/ ::testing::Values(true, false)));
#endif
}  // namespace
}  // namespace internal
}  // namespace gradients
}  // namespace tensorflow
<commit_msg>changed tolerance to 1e-2<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/c/eager/gradient_checker.h"

#include <memory>

#include "absl/types/span.h"
#include "tensorflow/c/eager/abstract_tensor_handle.h"
#include "tensorflow/c/eager/c_api_experimental.h"
#include "tensorflow/c/eager/c_api_unified_experimental.h"
#include "tensorflow/c/eager/c_api_unified_experimental_internal.h"
#include "tensorflow/c/eager/gradients.h"
#include "tensorflow/c/eager/gradients_internal.h"
#include "tensorflow/c/eager/gradients_util.h"
#include "tensorflow/c/eager/mnist_gradients_testutil.h"
#include "tensorflow/c/experimental/gradients/math_grad.h"
#include "tensorflow/c/experimental/gradients/nn_grad.h"
#include "tensorflow/c/experimental/ops/array_ops.h"
#include "tensorflow/c/tf_status_helper.h"
#include "tensorflow/c/tf_tensor.h"
#include "tensorflow/core/lib/llvm_rtti/llvm_rtti.h"
#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/test.h"

namespace tensorflow {
namespace gradients {
namespace internal {
namespace {

class GradientCheckerTest
    : public ::testing::TestWithParam<std::tuple<const char*, bool, bool>> {
 protected:
  void SetUp() override {
    TF_StatusPtr status(TF_NewStatus());
    TF_SetTracingImplementation(std::get<0>(GetParam()), status.get());
    Status s = StatusFromTF_Status(status.get());
    CHECK_EQ(errors::OK, s.code()) << s.error_message();
  }
};

Status RegisterGradients(GradientRegistry* registry) {
  TF_RETURN_IF_ERROR(registry->Register("MatMul", MatMulRegisterer));
  TF_RETURN_IF_ERROR(
      registry->Register("SparseSoftmaxCrossEntropyWithLogits",
                         SparseSoftmaxCrossEntropyLossRegisterer));
  return Status::OK();
}

TEST_P(GradientCheckerTest, TestGradCheckMatMul) {
  std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
      TF_NewStatus(), TF_DeleteStatus);
  AbstractContextPtr ctx;
  {
    AbstractContext* ctx_raw = nullptr;
    Status s =
        BuildImmediateExecutionContext(std::get<1>(GetParam()), &ctx_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    ctx.reset(ctx_raw);
  }

  float A_vals[] = {1.0f, 2.0f, 3.0f, 4.0f};
  int64_t A_dims[] = {2, 2};
  float B_vals[] = {.5f, -1.0f, 1.0f, 1.0f};
  int64_t B_dims[] = {2, 2};
  int num_dims = 2;

  AbstractTensorHandlePtr A =
      GetTensorHandleUtilFloat(ctx.get(), A_vals, A_dims, num_dims);
  AbstractTensorHandlePtr B =
      GetTensorHandleUtilFloat(ctx.get(), B_vals, B_dims, num_dims);

  std::vector<AbstractTensorHandle*> inputs;
  inputs.push_back(A.get());
  inputs.push_back(B.get());

  AbstractTensorHandle* grad_approx;
  Status s = CalcNumericalGrad(
      ctx.get(), MatMulModel, absl::MakeSpan(inputs), /*input_index=*/0,
      /*use_function=*/!std::get<2>(GetParam()), &grad_approx);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  TF_Tensor* gt;
  GetValue(grad_approx, &gt);
  float result_data[4] = {0};
  memcpy(&result_data[0], TF_TensorData(gt), TF_TensorByteSize(gt));

  float expected_dA[4] = {-.5f, 2.0f, -.5f, 2.0f};
  float tolerance = 1e-2;
  for (int j = 0; j < 4; j++) {
    ASSERT_NEAR(expected_dA[j], result_data[j], tolerance);
  }
  TF_DeleteTensor(gt);
}

TEST_P(GradientCheckerTest, TestGradCheckMul) {
  std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
      TF_NewStatus(), TF_DeleteStatus);

  AbstractContextPtr ctx;
  {
    AbstractContext* ctx_raw = nullptr;
    Status s =
        BuildImmediateExecutionContext(std::get<1>(GetParam()), &ctx_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    ctx.reset(ctx_raw);
  }

  AbstractTensorHandlePtr x;
  {
    AbstractTensorHandle* x_raw = nullptr;
    Status s = ScalarTensorHandle(ctx.get(), 2.0f, &x_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    x.reset(x_raw);
  }

  AbstractTensorHandlePtr y;
  {
    AbstractTensorHandle* y_raw = nullptr;
    Status s = ScalarTensorHandle(ctx.get(), 7.0f, &y_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    y.reset(y_raw);
  }

  // Will perform z = x*y.
  // dz/dx = y

  std::vector<AbstractTensorHandle*> inputs;
  inputs.push_back(x.get());
  inputs.push_back(y.get());
  float dapprox[1] = {0};
  AbstractTensorHandle* g;

  Status s = CalcNumericalGrad(ctx.get(), MulModel, absl::MakeSpan(inputs),
                               /*input_index=*/0,
                               /*use_function=*/!std::get<2>(GetParam()), &g);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  TF_Tensor* gt;
  GetValue(g, &gt);
  float result_data[1] = {0};
  memcpy(&result_data[0], TF_TensorData(gt), TF_TensorByteSize(gt));

  ASSERT_NEAR(result_data[0], 7.0f, /*tolerance=*/1e-2);
  TF_DeleteTensor(gt);
}

TEST_P(GradientCheckerTest, TestGradCheckSoftmax) {
  std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
      TF_NewStatus(), TF_DeleteStatus);

  /** Test to show how to use this API with analytical gradients:
   *
   *  We have `SoftmaxLossGradModel`, which is a wrapper for the
   *  Softmax analytical gradient found in c/experimental/nn_grads.
   *
   *  We will use the GradientChecker by applying finite differences
   *  to the forward pass wrapped in `SoftmaxModel` and verify that
   *  both the analytical and numerical gradients are relatively
   *  close.
   *
   */

  AbstractContextPtr ctx;
  {
    AbstractContext* ctx_raw = nullptr;
    Status s =
        BuildImmediateExecutionContext(std::get<1>(GetParam()), &ctx_raw);
    ASSERT_EQ(errors::OK, s.code()) << s.error_message();
    ctx.reset(ctx_raw);
  }

  // X = scores
  float X_vals[] = {1.0f, 2.0f, 3.0f, -5.0f, -4.0f, -3.0f, 2.0f, 0.0f, 1.0f};
  int64_t X_dims[] = {3, 3};
  int num_dims = 2;
  AbstractTensorHandlePtr X =
      GetTensorHandleUtilFloat(ctx.get(), X_vals, X_dims, num_dims);

  // y = labels
  int y_vals[] = {1, 0, 1};
  int64_t y_dims[] = {3};
  num_dims = sizeof(y_dims) / sizeof(y_dims[0]);
  AbstractTensorHandlePtr y =
      GetTensorHandleUtilInt(ctx.get(), y_vals, y_dims, num_dims);

  GradientRegistry registry;
  Status s = RegisterGradients(&registry);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  std::vector<AbstractTensorHandle*> inputs;
  inputs.push_back(X.get());
  inputs.push_back(y.get());

  // Run analytical gradient and get its data.
  std::vector<AbstractTensorHandle*> outputs(2);
  s = RunModel(SoftmaxLossGradModel, ctx.get(), absl::MakeSpan(inputs),
               absl::MakeSpan(outputs),
               /*use_function=*/!std::get<2>(GetParam()), registry);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  TF_Tensor* dX_tensor;
  s = GetValue(outputs[0], &dX_tensor);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  float danalytical[9] = {0};  // Contains data from analytical gradient.
  memcpy(&danalytical[0], TF_TensorData(dX_tensor),
         TF_TensorByteSize(dX_tensor));

  // Run numerical gradient approximation using the GradientChecker API.
  AbstractTensorHandle* g;  // Will contain numerical approximation data.
  s = CalcNumericalGrad(ctx.get(), SoftmaxModel, absl::MakeSpan(inputs),
                        /*input_index=*/0,
                        /*use_function=*/!std::get<2>(GetParam()), &g);
  ASSERT_EQ(errors::OK, s.code()) << s.error_message();

  TF_Tensor* gt;
  GetValue(g, &gt);
  float dnumerical[9] = {0};
  memcpy(&dnumerical[0], TF_TensorData(gt), TF_TensorByteSize(gt));

  // Now compare the two implementations:
  for (int j = 0; j < 9; j++) {
    ASSERT_NEAR(dnumerical[j], danalytical[j], /*tolerance=*/1e-2);
  }

  // Only Unref() first output as 2nd is nullptr grad for labels
  outputs[0]->Unref();
  TF_DeleteTensor(dX_tensor);
  TF_DeleteTensor(gt);
}

#ifdef PLATFORM_GOOGLE
INSTANTIATE_TEST_SUITE_P(
    UnifiedCAPI, GradientCheckerTest,
    ::testing::Combine(::testing::Values("graphdef"),
                       /*tfrt*/ ::testing::Values(false),
                       /*executing_eagerly*/ ::testing::Values(true, false)));
#else
INSTANTIATE_TEST_SUITE_P(
    UnifiedCAPI, GradientCheckerTest,
    ::testing::Combine(::testing::Values("graphdef"),
                       /*tfrt*/ ::testing::Values(false),
                       /*executing_eagerly*/ ::testing::Values(true, false)));
#endif
}  // namespace
}  // namespace internal
}  // namespace gradients
}  // namespace tensorflow
<|endoftext|>


<commit_before>// RUN: %clang_cc1 %s -fsyntax-only -verify
// RUN: cp %s %t
// RUN: not %clang_cc1 -x c++ -fixit %t
// RUN: %clang_cc1 -x c++ %t

struct S { static int a,b,c;};
int S::(a);  // expected-error{{unexpected parentheses after '::'}}
int S::(b;  // expected-error{{unexpected parentheses after '::'}}
int S::c;
int S::(*d);  // expected-error{{unexpected parentheses after '::'}}
int S::(*e;  // expected-error{{unexpected parentheses after '::'}}
int S::*f;
int g = S::(a);  // expected-error{{unexpected parentheses after '::'}}
int h = S::(b;  // expected-error{{unexpected parentheses after '::'}}
int i = S::c;

void foo() {
  int a;
  a = ::(g);  // expected-error{{unexpected parentheses after '::'}}
  a = ::(h;  // expected-error{{unexpected parentheses after '::'}}
  a = ::i;
}
<commit_msg>Fix test cases I broken when fixing grammar in diagnostic message.<commit_after>// RUN: %clang_cc1 %s -fsyntax-only -verify
// RUN: cp %s %t
// RUN: not %clang_cc1 -x c++ -fixit %t
// RUN: %clang_cc1 -x c++ %t

struct S { static int a,b,c;};
int S::(a);  // expected-error{{unexpected parenthesis after '::'}}
int S::(b;  // expected-error{{unexpected parenthesis after '::'}}
int S::c;
int S::(*d);  // expected-error{{unexpected parenthesis after '::'}}
int S::(*e;  // expected-error{{unexpected parenthesis after '::'}}
int S::*f;
int g = S::(a);  // expected-error{{unexpected parenthesis after '::'}}
int h = S::(b;  // expected-error{{unexpected parenthesis after '::'}}
int i = S::c;

void foo() {
  int a;
  a = ::(g);  // expected-error{{unexpected parenthesis after '::'}}
  a = ::(h;  // expected-error{{unexpected parenthesis after '::'}}
  a = ::i;
}
<|endoftext|>


<commit_before>#include <iostream>
#include "PolarisQuery.h"
#include "Poco/Net/TCPServer.h"
#include "ShipStatusConnection.h"
#include "PolarisShared.h"

using namespace std;
using Poco::Net::TCPServer;
using Poco::Net::ServerSocket;
using Poco::Net::TCPServerConnectionFactoryImpl;

int main(int argc, char** argv) {
    PolarisQuery app;
    return app.run(argc, argv);
}

int PolarisQuery::main(const std::vector<std::string> &args) {
    cout << "Polaris Server Emulator: Ship Query and Block Load balancer." << endl;
    cout << "Compiled against libpolaris_shared verion " << Polaris::GetLibVersion() << " at " << Polaris::GetTimeCompiled() << endl;
    std::vector<TCPServer *> servers;
    for (int i = 0; i < 10; i++) {
        servers.push_back(new TCPServer(new TCPServerConnectionFactoryImpl<ShipStatusConnection>, ServerSocket((Poco::UInt16) (12099 + (100 * i)))));
    }

    for(auto &i : servers) {
        i->start();
    }

    waitForTerminationRequest();

    for (auto &i : servers) {
        delete i;
    }

    return 0;
}<commit_msg>Add ShipBlockConnection<commit_after>#include <iostream>
#include "PolarisQuery.h"
#include "Poco/Net/TCPServer.h"
#include "ShipStatusConnection.h"
#include "PolarisShared.h"
#include "ShipBlockConnection.h"

using namespace std;
using Poco::Net::TCPServer;
using Poco::Net::ServerSocket;
using Poco::Net::TCPServerConnectionFactoryImpl;

int main(int argc, char** argv) {
    PolarisQuery app;
    return app.run(argc, argv);
}

int PolarisQuery::main(const std::vector<std::string> &args) {
    cout << "Polaris Server Emulator: Ship Query and Block Load balancer." << endl;
    cout << "Compiled against libpolaris_shared verion " << Polaris::GetLibVersion() << " at " << Polaris::GetTimeCompiled() << endl;
    std::vector<TCPServer *> servers;
    for (int i = 0; i < 10; i++) {
        servers.push_back(new TCPServer(new TCPServerConnectionFactoryImpl<ShipStatusConnection>, ServerSocket((Poco::UInt16) (12099 + (100 * i)))));
    }

    servers.push_back(new TCPServer(new TCPServerConnectionFactoryImpl<ShipBlockConnection>, ServerSocket((Poco::UInt16) 12200))); // Ship 2 block balancer

    for(auto &i : servers) {
        i->start();
    }

    waitForTerminationRequest();

    for (auto &i : servers) {
        delete i;
    }

    return 0;
}<|endoftext|>


<commit_before>//-----------------------------------
// Copyright Pierric Gimmig 2013-2017
//-----------------------------------

#include "BpfTrace.h"
#include "LinuxUtils.h"
#include "CoreApp.h"
#include "Capture.h"
#include "OrbitModule.h"
#include "OrbitProcess.h"
#include "Params.h"
#include "ScopeTimer.h"
#include "Utils.h"
#include <fstream>
#include <sstream>

#if __linux__
#include "BpfTraceVisitor.h"
#include "LinuxPerfEvent.h"
#include "LinuxPerfEventProcessor.h"
#include "LinuxPerfRingBuffer.h"
#include "LinuxPerfUtils.h"
#include "OrbitFunction.h"
#include "Pdb.h"
#include <linux/perf_event.h>
#include <linux/version.h>
#include <map>
#include <vector>

using namespace LinuxPerfUtils;
#endif

//-----------------------------------------------------------------------------
BpfTrace::BpfTrace(Callback a_Callback)
{
#if __linux__

    // TODO: we shouldn't hijack the BpfTrace class and move perf_event related
    //       code to its own class.

    // Uprobe perf_events not supported until Kernel 4.17
    if( LinuxPerfUtils::supports_perf_event_uprobes() )
    {
        m_UsePerfEvents = !GParams.m_UseBpftrace && !GParams.m_BpftraceCallstacks;
    }
    else
    {
        m_UsePerfEvents = false;
    }

    PRINT_VAR(m_UsePerfEvents);

    m_Callback = a_Callback ? a_Callback : [this](const std::string& a_Buffer)
    {
        if( GParams.m_BpftraceCallstacks )
            CommandCallbackWithCallstacks(a_Buffer);
        else
            CommandCallback(a_Buffer);
    };

    m_ScriptFileName = ws2s(Path::GetBasePath()) + "orbit.bt";
#endif
}

//-----------------------------------------------------------------------------
void BpfTrace::Start()
{
#if __linux__
    m_ExitRequested = false;
    m_TimerStacks.clear();

    if( !WriteBpfScript() )
        return;

    m_BpfCommand = std::string("bpftrace ") + m_ScriptFileName;

    if ( !m_UsePerfEvents ) {
        m_Thread = std::make_shared<std::thread>
            ( &LinuxUtils::StreamCommandOutput
            , m_BpfCommand.c_str()
            , m_Callback
            , &m_ExitRequested );
    }
    else
    {
        m_Thread = std::make_shared<std::thread>
            ( BpfTrace::RunPerfEventOpen
            , &m_ExitRequested );
    }

    m_Thread->detach();
#endif
}

//-----------------------------------------------------------------------------
void BpfTrace::Stop()
{
    m_ExitRequested = true;
}

//-----------------------------------------------------------------------------
std::string BpfTrace::GetBpfScript()
{
    if (!m_Script.empty())
    {
        return m_Script;
    }

    std::stringstream ss;

    for (Function *func : Capture::GTargetProcess->GetFunctions())
    {
        if (func->IsSelected())
        {
            uint64_t virtual_address = (uint64_t)func->GetVirtualAddress();
            Capture::GSelectedFunctionsMap[func->m_Address] = func;

            if( GParams.m_BpftraceCallstacks )
            {
                ss << "   uprobe:" << func->m_Probe << R"({ printf("b )" << std::to_string(virtual_address) << R"( %u %lld\n%s\n\nd\n\n", tid, nsecs, ustack(perf)); })" << std::endl;
            }
            else
            {
                ss << "   uprobe:" << func->m_Probe << R"({ printf("b )" << std::to_string(virtual_address) << R"( %u %lld\n", tid, nsecs); })" << std::endl;
            }

            ss << "uretprobe:" << func->m_Probe << R"({ printf("e )" << std::to_string(virtual_address) << R"( %u %lld\n", tid, nsecs); })" << std::endl;
        }
    }

    return ss.str();
}

//-----------------------------------------------------------------------------
bool BpfTrace::WriteBpfScript()
{
    std::string script = GetBpfScript();
    if (script.empty())
        return false;

    std::ofstream outFile;
    outFile.open(m_ScriptFileName);
    if (outFile.fail())
        return false;

    outFile << script;
    outFile.close();
    return true;
}

//-----------------------------------------------------------------------------
uint64_t BpfTrace::ProcessString(const std::string& a_String)
{
    auto hash = StringHash(a_String);
    if (m_StringMap.find(hash) == m_StringMap.end())
    {
        m_StringMap[hash] = a_String;
    }

    return hash;
}

//-----------------------------------------------------------------------------
void BpfTrace::CommandCallback(const std::string& a_Line)
{
    auto tokens = Tokenize(a_Line);
    const std::string& mode             = tokens[0];
    const std::string& functionAddress  = tokens[1];
    const std::string& threadName       = tokens[2];
    const std::string& timestamp        = tokens[3];

    bool isBegin = mode == "b";
    bool isEnd   = !isBegin;

    if (isBegin)
    {
        Timer timer;
        timer.m_TID = atoi(threadName.c_str());
        uint64_t nanos = std::stoull(timestamp);
        timer.m_Start = nanos;
        timer.m_Depth = (uint8_t)m_TimerStacks[threadName].size();
        timer.m_FunctionAddress = std::stoull(functionAddress);
        m_TimerStacks[threadName].push_back(timer);
    }

    if (isEnd)
    {
        std::vector<Timer>& timers = m_TimerStacks[threadName];
        if (timers.size())
        {
            Timer& timer = timers.back();
            uint64_t nanos = std::stoull(timestamp);
            timer.m_End = nanos;
            GCoreApp->ProcessTimer(&timer, functionAddress);
            timers.pop_back();
        }
    }
}

//-----------------------------------------------------------------------------
void BpfTrace::CommandCallbackWithCallstacks(const std::string& a_Line)
{
    if (a_Line.empty() || a_Line == "\n")
        return;

    auto tokens = Tokenize(a_Line);

    const std::string& mode = tokens[0];
    bool isBegin = mode == "b";
    bool isEnd   = mode == "e";

    bool isStackLine = StartsWith(a_Line, "\t");
    bool isEndOfStack = a_Line == "d\n";

    if (!isBegin && !isEnd && !isStackLine && !isEndOfStack)
    {
        if (StartsWith(a_Line, "Lost"))
        {
            PRINT(a_Line.c_str());
            return;
        }

        if (StartsWith(a_Line, "Attaching"))
            return;

        // if the line does not start with one of the above,
        // we might have a broken line, e.g. due to a small buffer
        PRINT("read unexpected line:%s\nthe buffer might be to small.", a_Line.c_str());
        return;
    }

    if (isStackLine) {
        const std::string& addressStr = LTrim(tokens[0]);
        uint64_t address = std::stoull(addressStr, nullptr, 16);

        std::string function = tokens[1];

        std::string moduleRaw = tokens[2];
        std::string module = Replace(moduleRaw.substr(1), ")\n", "");

        // TODO: this is copy&paste from LinuxPerf.cpp
        std::wstring moduleName = ToLower(Path::GetFileName(s2ws(module)));
        std::shared_ptr<Module> moduleFromName = Capture::GTargetProcess->GetModuleFromName( ws2s(moduleName) );

        if( moduleFromName )
        {
            uint64_t new_address = moduleFromName->ValidateAddress(address);
            address = new_address;
        }

        m_CallStack.m_Data.push_back(address);
        if( Capture::GTargetProcess && !Capture::GTargetProcess->HasSymbol(address))
        {
            GCoreApp->AddSymbol(address, module, function);
        }

        return;
    }

    if (isEndOfStack)
    {
        if ( m_CallStack.m_Data.size() ) {
            m_CallStack.m_Depth = (uint32_t)m_CallStack.m_Data.size();
            m_CallStack.m_ThreadId = atoi(m_LastThreadName.c_str());
            std::vector<Timer>& timers = m_TimerStacks[m_LastThreadName];
            if (timers.size())
            {
                Timer& timer = timers.back();
                timer.m_CallstackHash = m_CallStack.Hash();
                GCoreApp->ProcessCallStack( m_CallStack );
            }
        }

        m_CallStack.Clear();
        m_LastThreadName = "";
        return;
    }

    const std::string& functionAddress  = tokens[1];
    const std::string& threadName       = tokens[2];
    const std::string& timestamp        = tokens[3];

    m_LastThreadName = threadName;

    if (isBegin)
    {
        Timer timer;
        timer.m_TID = atoi(threadName.c_str());
        uint64_t nanos = std::stoull(timestamp);
        timer.m_Start = nanos;
        timer.m_Depth = (uint8_t)m_TimerStacks[threadName].size();
        timer.m_FunctionAddress = std::stoull(functionAddress);
        m_TimerStacks[threadName].push_back(timer);
        return;
    }

    if (isEnd)
    {
        std::vector<Timer>& timers = m_TimerStacks[threadName];
        if (timers.size())
        {
            Timer& timer = timers.back();
            uint64_t nanos = std::stoull(timestamp);
            timer.m_End = nanos;
            GCoreApp->ProcessTimer(&timer, functionAddress);
            timers.pop_back();
        }
        return;
    }
}

//-----------------------------------------------------------------------------
void BpfTrace::RunPerfEventOpen(bool* a_ExitRequested)
{
    #if __linux__
    SCOPE_TIMER_FUNC;
    int ROUND_ROBIN_BATCH_SIZE = 5;
    std::vector<uint64_t> fds;
    std::map<Function*, LinuxPerfRingBuffer> uprobe_ring_buffers;
    std::map<Function*, LinuxPerfRingBuffer> uretprobe_ring_buffers;

    for (const auto& pair : Capture::GSelectedFunctionsMap)
    {
        // gather function information
        Function* function = pair.second;
        uint64_t offset = function->m_Address;
        std::string module = ws2s(function->m_Pdb->GetFileName());

        // create uprobe for that function on that PID profiling all cpus
        uint64_t uprobe_fd = 0;
        if (GParams.m_BpftraceCallstacks)
            uprobe_fd = LinuxPerfUtils::uprobe_event_open(
                module.c_str(), offset, Capture::GTargetProcess->GetID(), -1,
                PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER
            );
        else
            uprobe_fd = LinuxPerfUtils::uprobe_event_open(
                module.c_str(), offset, Capture::GTargetProcess->GetID(), -1
            );

        uprobe_ring_buffers.emplace(function, uprobe_fd);
        fds.push_back(uprobe_fd);

        // create uretprobe for that function on that PID profiling all cpus
        uint64_t uretprobe_fd = LinuxPerfUtils::uretprobe_event_open(
            module.c_str(), offset, Capture::GTargetProcess->GetID(), -1
        );
        uretprobe_ring_buffers.emplace(function, uretprobe_fd);
        fds.push_back(uretprobe_fd);

        // start capturing
        LinuxPerfUtils::start_capturing(uprobe_fd);
        LinuxPerfUtils::start_capturing(uretprobe_fd);
    }

    LinuxPerfEventProcessor event_buffer(std::make_unique<BpfTraceVisitor>());

    bool new_events = false;

    while( !(*a_ExitRequested) )
    {
        SCOPE_TIMER("Perf events consume iteration");
        // Lets sleep a bit, such that we are not constantly reading from the buffers
        // and thus wasting cpu time. 10000 microseconds are still small enough to
        // not have our buffers overflown and therefore losing events.
        if (!new_events)
        {
            usleep(10000);
        }

        new_events = false;

        // read from all ring buffers, create events and store them in the event_queue
        // TODO: implement a better scheduling strategy.
        for (auto& pair : uprobe_ring_buffers)
        {
            auto& ring_buffer = pair.second;
            int i = 0;
            // read everything that is new
            while (ring_buffer.HasNewData() && i < ROUND_ROBIN_BATCH_SIZE)
            {
                i++;
                new_events = true;
                perf_event_header header{};
                ring_buffer.ReadHeader(&header);

                // perf_event_header::type contains the type of record,
                // defined in enum perf_event_type in perf_event.h.
                switch (header.type)
                {
                case PERF_RECORD_SAMPLE:
                    {
                        PRINT("Consuming uprobe record...");
                        if (GParams.m_BpftraceCallstacks)
                        {
                            auto record = ring_buffer.ConsumeRecord<LinuxUprobeEventWithStack>(header);
                            record.SetFunction(pair.first);
                            event_buffer.Push(std::make_unique<LinuxUprobeEventWithStack>(std::move(record)));
                        }
                        else
                        {
                            auto record = ring_buffer.ConsumeRecord<LinuxUprobeEvent>(header);
                            record.SetFunction(pair.first);
                            event_buffer.Push(std::make_unique<LinuxUprobeEvent>(std::move(record)));
                        }

                    }
                    break;
                 case PERF_RECORD_LOST:
                    {
                        auto lost = ring_buffer.ConsumeRecord<LinuxPerfLostEvent>(header);
                        PRINT("Lost %u Events\n", lost.Lost());
                    }
                    break;
                default:
                    PRINT("Unexpected Perf Sample Type: %u", header.type);
                    ring_buffer.SkipRecord(header);
                    break;
                }
            }
        }

        for (auto& pair : uretprobe_ring_buffers)
        {
            auto& ring_buffer = pair.second;
            int i = 0;
            // read everything that is new
            while (ring_buffer.HasNewData() && i < ROUND_ROBIN_BATCH_SIZE)
            {
                i++;
                new_events = true;
                perf_event_header header{};
                ring_buffer.ReadHeader(&header);

                // perf_event_header::type contains the type of record,
                // defined in enum perf_event_type in perf_event.h.
                switch (header.type)
                {
                case PERF_RECORD_SAMPLE:
                    {
                        PRINT("Consuming uretprobe record...");
                        auto record = ring_buffer.ConsumeRecord<LinuxUretprobeEvent>(header);
                        record.SetFunction(pair.first);
                        event_buffer.Push(std::make_unique<LinuxUretprobeEvent>(std::move(record)));
                    }
                    break;
                 case PERF_RECORD_LOST:
                    {
                        auto lost = ring_buffer.ConsumeRecord<LinuxPerfLostEvent>(header);
                        PRINT("Lost %u Events\n", lost.Lost());
                    }
                    break;
                default:
                    PRINT("Unexpected Perf Sample Type: %u", header.type);
                    ring_buffer.SkipRecord(header);
                    break;
                }
            }
        }

        event_buffer.ProcessTillOffset();
    }

    for (auto fd : fds)
    {
        LinuxPerfUtils::stop_capturing(fd);
    }

    event_buffer.ProcessAll();
    #endif
}
<commit_msg>Fix compilation.<commit_after>//-----------------------------------
// Copyright Pierric Gimmig 2013-2017
//-----------------------------------

#include "BpfTrace.h"
#include "LinuxUtils.h"
#include "CoreApp.h"
#include "Capture.h"
#include "OrbitModule.h"
#include "OrbitProcess.h"
#include "Params.h"
#include "ScopeTimer.h"
#include "Utils.h"
#include <fstream>
#include <sstream>

#if __linux__
#include "BpfTraceVisitor.h"
#include "LinuxPerfEvent.h"
#include "LinuxPerfEventProcessor.h"
#include "LinuxPerfRingBuffer.h"
#include "LinuxPerfUtils.h"
#include "OrbitFunction.h"
#include "Pdb.h"
#include <linux/perf_event.h>
#include <linux/version.h>
#include <map>
#include <vector>

using namespace LinuxPerfUtils;
#endif

//-----------------------------------------------------------------------------
BpfTrace::BpfTrace(Callback a_Callback)
{
#if __linux__

    // TODO: we shouldn't hijack the BpfTrace class and move perf_event related
    //       code to its own class.

    // Uprobe perf_events not supported until Kernel 4.17
    if( LinuxPerfUtils::supports_perf_event_uprobes() )
    {
        m_UsePerfEvents = !GParams.m_UseBpftrace && !GParams.m_BpftraceCallstacks;
    }
    else
    {
        m_UsePerfEvents = false;
    }

    PRINT_VAR(m_UsePerfEvents);

    m_Callback = a_Callback ? a_Callback : [this](const std::string& a_Buffer)
    {
        if( GParams.m_BpftraceCallstacks )
            CommandCallbackWithCallstacks(a_Buffer);
        else
            CommandCallback(a_Buffer);
    };

    m_ScriptFileName = ws2s(Path::GetBasePath()) + "orbit.bt";
#endif
}

//-----------------------------------------------------------------------------
void BpfTrace::Start()
{
#if __linux__
    m_ExitRequested = false;
    m_TimerStacks.clear();

    if( !WriteBpfScript() )
        return;

    m_BpfCommand = std::string("bpftrace ") + m_ScriptFileName;

    if ( !m_UsePerfEvents ) {
        m_Thread = std::make_shared<std::thread>
            ( &LinuxUtils::StreamCommandOutput
            , m_BpfCommand.c_str()
            , m_Callback
            , &m_ExitRequested );
    }
    else
    {
        m_Thread = std::make_shared<std::thread>
            ( BpfTrace::RunPerfEventOpen
            , &m_ExitRequested );
    }

    m_Thread->detach();
#endif
}

//-----------------------------------------------------------------------------
void BpfTrace::Stop()
{
    m_ExitRequested = true;
}

//-----------------------------------------------------------------------------
std::string BpfTrace::GetBpfScript()
{
    if (!m_Script.empty())
    {
        return m_Script;
    }

    std::stringstream ss;

    for (Function *func : Capture::GTargetProcess->GetFunctions())
    {
        if (func->IsSelected())
        {
            uint64_t virtual_address = (uint64_t)func->GetVirtualAddress();
            Capture::GSelectedFunctionsMap[func->m_Address] = func;

            if( GParams.m_BpftraceCallstacks )
            {
                ss << "   uprobe:" << func->m_Probe << R"({ printf("b )" << std::to_string(virtual_address) << R"( %u %lld\n%s\n\nd\n\n", tid, nsecs, ustack(perf)); })" << std::endl;
            }
            else
            {
                ss << "   uprobe:" << func->m_Probe << R"({ printf("b )" << std::to_string(virtual_address) << R"( %u %lld\n", tid, nsecs); })" << std::endl;
            }

            ss << "uretprobe:" << func->m_Probe << R"({ printf("e )" << std::to_string(virtual_address) << R"( %u %lld\n", tid, nsecs); })" << std::endl;
        }
    }

    return ss.str();
}

//-----------------------------------------------------------------------------
bool BpfTrace::WriteBpfScript()
{
    std::string script = GetBpfScript();
    if (script.empty())
        return false;

    std::ofstream outFile;
    outFile.open(m_ScriptFileName);
    if (outFile.fail())
        return false;

    outFile << script;
    outFile.close();
    return true;
}

//-----------------------------------------------------------------------------
uint64_t BpfTrace::ProcessString(const std::string& a_String)
{
    auto hash = StringHash(a_String);
    if (m_StringMap.find(hash) == m_StringMap.end())
    {
        m_StringMap[hash] = a_String;
    }

    return hash;
}

//-----------------------------------------------------------------------------
void BpfTrace::CommandCallback(const std::string& a_Line)
{
    auto tokens = Tokenize(a_Line);
    const std::string& mode             = tokens[0];
    const std::string& functionAddress  = tokens[1];
    const std::string& threadName       = tokens[2];
    const std::string& timestamp        = tokens[3];

    bool isBegin = mode == "b";
    bool isEnd   = !isBegin;

    if (isBegin)
    {
        Timer timer;
        timer.m_TID = atoi(threadName.c_str());
        uint64_t nanos = std::stoull(timestamp);
        timer.m_Start = nanos;
        timer.m_Depth = (uint8_t)m_TimerStacks[threadName].size();
        timer.m_FunctionAddress = std::stoull(functionAddress);
        m_TimerStacks[threadName].push_back(timer);
    }

    if (isEnd)
    {
        std::vector<Timer>& timers = m_TimerStacks[threadName];
        if (timers.size())
        {
            Timer& timer = timers.back();
            uint64_t nanos = std::stoull(timestamp);
            timer.m_End = nanos;
            GCoreApp->ProcessTimer(&timer, functionAddress);
            timers.pop_back();
        }
    }
}

//-----------------------------------------------------------------------------
void BpfTrace::CommandCallbackWithCallstacks(const std::string& a_Line)
{
    if (a_Line.empty() || a_Line == "\n")
        return;

    auto tokens = Tokenize(a_Line);

    const std::string& mode = tokens[0];
    bool isBegin = mode == "b";
    bool isEnd   = mode == "e";

    bool isStackLine = StartsWith(a_Line, "\t");
    bool isEndOfStack = a_Line == "d\n";

    if (!isBegin && !isEnd && !isStackLine && !isEndOfStack)
    {
        if (StartsWith(a_Line, "Lost"))
        {
            PRINT(a_Line.c_str());
            return;
        }

        if (StartsWith(a_Line, "Attaching"))
            return;

        // if the line does not start with one of the above,
        // we might have a broken line, e.g. due to a small buffer
        PRINT("read unexpected line:%s\nthe buffer might be to small.", a_Line.c_str());
        return;
    }

    if (isStackLine) {
        const std::string& addressStr = LTrim(tokens[0]);
        uint64_t address = std::stoull(addressStr, nullptr, 16);

        std::string function = tokens[1];

        std::string moduleRaw = tokens[2];
        std::string module = Replace(moduleRaw.substr(1), ")\n", "");

        // TODO: this is copy&paste from LinuxPerf.cpp
        std::wstring moduleName = ToLower(Path::GetFileName(s2ws(module)));
        std::shared_ptr<Module> moduleFromName = Capture::GTargetProcess->GetModuleFromName( ws2s(moduleName) );

        if( moduleFromName )
        {
            uint64_t new_address = moduleFromName->ValidateAddress(address);
            address = new_address;
        }

        m_CallStack.m_Data.push_back(address);
        if( Capture::GTargetProcess && !Capture::GTargetProcess->HasSymbol(address))
        {
            GCoreApp->AddSymbol(address, module, function);
        }

        return;
    }

    if (isEndOfStack)
    {
        if ( m_CallStack.m_Data.size() ) {
            m_CallStack.m_Depth = (uint32_t)m_CallStack.m_Data.size();
            m_CallStack.m_ThreadId = atoi(m_LastThreadName.c_str());
            std::vector<Timer>& timers = m_TimerStacks[m_LastThreadName];
            if (timers.size())
            {
                Timer& timer = timers.back();
                timer.m_CallstackHash = m_CallStack.Hash();
                GCoreApp->ProcessCallStack( m_CallStack );
            }
        }

        m_CallStack.Clear();
        m_LastThreadName = "";
        return;
    }

    const std::string& functionAddress  = tokens[1];
    const std::string& threadName       = tokens[2];
    const std::string& timestamp        = tokens[3];

    m_LastThreadName = threadName;

    if (isBegin)
    {
        Timer timer;
        timer.m_TID = atoi(threadName.c_str());
        uint64_t nanos = std::stoull(timestamp);
        timer.m_Start = nanos;
        timer.m_Depth = (uint8_t)m_TimerStacks[threadName].size();
        timer.m_FunctionAddress = std::stoull(functionAddress);
        m_TimerStacks[threadName].push_back(timer);
        return;
    }

    if (isEnd)
    {
        std::vector<Timer>& timers = m_TimerStacks[threadName];
        if (timers.size())
        {
            Timer& timer = timers.back();
            uint64_t nanos = std::stoull(timestamp);
            timer.m_End = nanos;
            GCoreApp->ProcessTimer(&timer, functionAddress);
            timers.pop_back();
        }
        return;
    }
}

//-----------------------------------------------------------------------------
void BpfTrace::RunPerfEventOpen(bool* a_ExitRequested)
{
    #if __linux__
    SCOPE_TIMER_FUNC;
    int ROUND_ROBIN_BATCH_SIZE = 5;
    std::vector<uint64_t> fds;
    std::map<Function*, LinuxPerfRingBuffer> uprobe_ring_buffers;
    std::map<Function*, LinuxPerfRingBuffer> uretprobe_ring_buffers;

    for (const auto& pair : Capture::GSelectedFunctionsMap)
    {
        // gather function information
        Function* function = pair.second;
        uint64_t offset = function->m_Address;
        std::string module = ws2s(function->m_Pdb->GetFileName());

        // create uprobe for that function on that PID profiling all cpus
        uint64_t uprobe_fd = 0;
        if (GParams.m_BpftraceCallstacks)
            uprobe_fd = LinuxPerfUtils::uprobe_event_open(
                module.c_str(), offset, Capture::GTargetProcess->GetID(), -1,
                PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER
            );
        else
            uprobe_fd = LinuxPerfUtils::uprobe_event_open(
                module.c_str(), offset, Capture::GTargetProcess->GetID(), -1
            );

        uprobe_ring_buffers.emplace(function, uprobe_fd);
        fds.push_back(uprobe_fd);

        // create uretprobe for that function on that PID profiling all cpus
        uint64_t uretprobe_fd = LinuxPerfUtils::uretprobe_event_open(
            module.c_str(), offset, Capture::GTargetProcess->GetID(), -1
        );
        uretprobe_ring_buffers.emplace(function, uretprobe_fd);
        fds.push_back(uretprobe_fd);

        // start capturing
        LinuxPerfUtils::start_capturing(uprobe_fd);
        LinuxPerfUtils::start_capturing(uretprobe_fd);
    }

    LinuxPerfEventProcessor event_buffer(std::make_unique<BpfTraceVisitor>());

    bool new_events = false;

    while( !(*a_ExitRequested) )
    {
        SCOPE_TIMER_LOG("Perf events consume iteration");
        // Lets sleep a bit, such that we are not constantly reading from the buffers
        // and thus wasting cpu time. 10000 microseconds are still small enough to
        // not have our buffers overflown and therefore losing events.
        if (!new_events)
        {
            usleep(10000);
        }

        new_events = false;

        // read from all ring buffers, create events and store them in the event_queue
        // TODO: implement a better scheduling strategy.
        for (auto& pair : uprobe_ring_buffers)
        {
            auto& ring_buffer = pair.second;
            int i = 0;
            // read everything that is new
            while (ring_buffer.HasNewData() && i < ROUND_ROBIN_BATCH_SIZE)
            {
                i++;
                new_events = true;
                perf_event_header header{};
                ring_buffer.ReadHeader(&header);

                // perf_event_header::type contains the type of record,
                // defined in enum perf_event_type in perf_event.h.
                switch (header.type)
                {
                case PERF_RECORD_SAMPLE:
                    {
                        PRINT("Consuming uprobe record...");
                        if (GParams.m_BpftraceCallstacks)
                        {
                            auto record = ring_buffer.ConsumeRecord<LinuxUprobeEventWithStack>(header);
                            record.SetFunction(pair.first);
                            event_buffer.Push(std::make_unique<LinuxUprobeEventWithStack>(std::move(record)));
                        }
                        else
                        {
                            auto record = ring_buffer.ConsumeRecord<LinuxUprobeEvent>(header);
                            record.SetFunction(pair.first);
                            event_buffer.Push(std::make_unique<LinuxUprobeEvent>(std::move(record)));
                        }

                    }
                    break;
                 case PERF_RECORD_LOST:
                    {
                        auto lost = ring_buffer.ConsumeRecord<LinuxPerfLostEvent>(header);
                        PRINT("Lost %u Events\n", lost.Lost());
                    }
                    break;
                default:
                    PRINT("Unexpected Perf Sample Type: %u", header.type);
                    ring_buffer.SkipRecord(header);
                    break;
                }
            }
        }

        for (auto& pair : uretprobe_ring_buffers)
        {
            auto& ring_buffer = pair.second;
            int i = 0;
            // read everything that is new
            while (ring_buffer.HasNewData() && i < ROUND_ROBIN_BATCH_SIZE)
            {
                i++;
                new_events = true;
                perf_event_header header{};
                ring_buffer.ReadHeader(&header);

                // perf_event_header::type contains the type of record,
                // defined in enum perf_event_type in perf_event.h.
                switch (header.type)
                {
                case PERF_RECORD_SAMPLE:
                    {
                        PRINT("Consuming uretprobe record...");
                        auto record = ring_buffer.ConsumeRecord<LinuxUretprobeEvent>(header);
                        record.SetFunction(pair.first);
                        event_buffer.Push(std::make_unique<LinuxUretprobeEvent>(std::move(record)));
                    }
                    break;
                 case PERF_RECORD_LOST:
                    {
                        auto lost = ring_buffer.ConsumeRecord<LinuxPerfLostEvent>(header);
                        PRINT("Lost %u Events\n", lost.Lost());
                    }
                    break;
                default:
                    PRINT("Unexpected Perf Sample Type: %u", header.type);
                    ring_buffer.SkipRecord(header);
                    break;
                }
            }
        }

        event_buffer.ProcessTillOffset();
    }

    for (auto fd : fds)
    {
        LinuxPerfUtils::stop_capturing(fd);
    }

    event_buffer.ProcessAll();
    #endif
}
<|endoftext|>