<commit_before>//
// Created by Gerard Canal <gcanal@iri.upc.edu> on 11/10/18.
//

#include <rosplan_knowledge_msgs/GetAttributeService.h>
#include "rosplan_planning_system/PlanDispatch/PlanDispatcher.h"

namespace KCL_rosplan {
    PlanDispatcher::PlanDispatcher(ros::NodeHandle& nh): as_(nh, "dispatch_plan_action", false) {
        node_handle = &nh;

        kb_ = "knowledge_base";
        node_handle->getParam("knowledge_base", kb_);

        std::stringstream ss;
        ss << "/" << kb_ << "/query_state";
        queryKnowledgeClient = node_handle->serviceClient<rosplan_knowledge_msgs::KnowledgeQueryService>(ss.str());
        ss.str("");
        ss << "/" << kb_ << "/domain/operator_details";
        queryDomainClient = node_handle->serviceClient<rosplan_knowledge_msgs::GetDomainOperatorDetailsService>(ss.str());
        ss.str("");
        ss << "/" << kb_ << "/state/goals";
        get_goals = node_handle->serviceClient<rosplan_knowledge_msgs::GetAttributeService>(ss.str());
        ss.str("");

        // Register actionlib callbacks
        as_.registerGoalCallback(boost::bind(&KCL_rosplan::PlanDispatcher::dispatchPlanActionlib, this));
        as_.registerPreemptCallback(boost::bind(&KCL_rosplan::PlanDispatcher::cancelDispatch, this));
        as_.start(); // Start the action

        // start the plan parsing services
        service1 = nh.advertiseService("dispatch_plan", &KCL_rosplan::PlanDispatcher::dispatchPlanService, this);
        service2 = nh.advertiseService("cancel_dispatch", &KCL_rosplan::PlanDispatcher::cancelDispatchService, this);
        dispatching = false;

        // Action topics
        action_dispatch_topic = "action_dispatch";
        action_feedback_topic = "action_feedback";
        nh.getParam("action_dispatch_topic", action_dispatch_topic);
        nh.getParam("action_feedback_topic", action_feedback_topic);
        action_dispatch_publisher = node_handle->advertise<rosplan_dispatch_msgs::ActionDispatch>(action_dispatch_topic, 1, true);
        action_feedback_publisher = node_handle->advertise<rosplan_dispatch_msgs::ActionFeedback>(action_feedback_topic, 1, true);
    }

    void PlanDispatcher::publishFeedback(const rosplan_dispatch_msgs::ActionFeedback &fb) {
        if (as_.isActive()) { // Action server is the one dispatching
            rosplan_dispatch_msgs::NonBlockingDispatchFeedback afeedback;
            afeedback.feedback = fb;
            as_.publishFeedback(afeedback);
        }
        // Publish feedback in topic anyway...
        action_feedback_publisher.publish(fb);
    }

    bool PlanDispatcher::cancelDispatch() {
        if (as_.isNewGoalAvailable()) { // Preempt caused by sending of new goal, ignore it
            //ROS_WARN("KCL: (%s) Got a new dispatch request but a plan is already being dispatched!", ros::this_node::getName().c_str());
            // Don't cancel a goal as it is being preempted because of a new goal sent
            return false;
        }
        ROS_INFO("KCL: (%s) Cancel plan command received.", ros::this_node::getName().c_str());
        if (as_.isActive()) as_.setPreempted();
        plan_cancelled = true;
        return true;
    }

    bool PlanDispatcher::cancelDispatchService(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res) {
        return cancelDispatch();
    }

    /*--------------------*/
    /* Dispatch interface */
    /*--------------------*/

    /**
     * plan dispatch service method (1)
     * dispatches plan as a service
     * @returns True iff every action was dispatched and returned success.
     */
    bool PlanDispatcher::dispatchPlanService(rosplan_dispatch_msgs::DispatchService::Request &req, rosplan_dispatch_msgs::DispatchService::Response &res) {
        if (dispatching) return false;
        dispatching = true;
        bool success = dispatchPlan(mission_start_time,ros::WallTime::now().toSec());
        dispatching = false;
        reset();
        res.success = success;
        res.goal_achieved = goalAchieved();
        return true;
    }

    /**
     * plan dispatch action method (2)
     * dispatches plan as a service
     * @returns True iff every action was dispatched and returned success.
     */
    void PlanDispatcher::dispatchPlanActionlib() {
        if (as_.isActive() or dispatching) {
            ROS_WARN("KCL: (%s) Got a new dispatch request but a plan is already being dispatched! It will be ignored", ros::this_node::getName().c_str());
        }
        else {
            as_.acceptNewGoal();
            dispatching = true;
            bool success = dispatchPlan(mission_start_time, ros::WallTime::now().toSec());
            dispatching = false;
            reset();
            rosplan_dispatch_msgs::NonBlockingDispatchResult res;
            res.success = success;
            res.goal_achieved = goalAchieved();
            as_.setSucceeded(res);
        }
    }

    void PlanDispatcher::reset() {
        replan_requested = false;
        dispatch_paused = false;
        plan_cancelled = false;
        action_received.clear();
        action_completed.clear();
        plan_received = false;
        dispatching = false;
    }

    /**
	 *	Returns true of the actions preconditions are true in the current state. Calls the Knowledge Base.
	 */
    bool PlanDispatcher::checkPreconditions(rosplan_dispatch_msgs::ActionDispatch msg) {

        // get domain opertor details
        rosplan_knowledge_msgs::GetDomainOperatorDetailsService srv;
        srv.request.name = msg.name;
        if(!queryDomainClient.call(srv)) {
            ROS_ERROR("KCL: (%s) could not call Knowledge Base for operator details, %s", ros::this_node::getName().c_str(), msg.name.c_str());
            return false;
        }

        // setup service call
        rosplan_knowledge_msgs::DomainOperator op = srv.response.op;
        rosplan_knowledge_msgs::KnowledgeQueryService querySrv;

        // iterate through conditions
        std::vector<rosplan_knowledge_msgs::DomainFormula>::iterator cit = op.at_start_simple_condition.begin();
        for(; cit!=op.at_start_simple_condition.end(); cit++) {

            // create condition
            rosplan_knowledge_msgs::KnowledgeItem condition;
            condition.knowledge_type = rosplan_knowledge_msgs::KnowledgeItem::FACT;
            condition.attribute_name = cit->name;

            // populate parameters
            for(int i=0; i<cit->typed_parameters.size(); i++) {

                // set parameter label to predicate label
                diagnostic_msgs::KeyValue param;
                param.key = cit->typed_parameters[i].key;

                // search for correct operator parameter value
                for(int j=0; j<msg.parameters.size() && j<op.formula.typed_parameters.size(); j++) {
                    if(op.formula.typed_parameters[j].key == cit->typed_parameters[i].key) {
                        param.value = msg.parameters[j].value;
                    }
                }
                condition.values.push_back(param);
            }
            querySrv.request.knowledge.push_back(condition);
        }

        // check conditions in knowledge base
        if (queryKnowledgeClient.call(querySrv)) {

            if(!querySrv.response.all_true) {
                std::vector<rosplan_knowledge_msgs::KnowledgeItem>::iterator kit;
                for(kit=querySrv.response.false_knowledge.begin(); kit != querySrv.response.false_knowledge.end(); kit++)
                    ROS_INFO("KCL: (%s) Precondition not achieved: %s", ros::this_node::getName().c_str(), kit->attribute_name.c_str());
            }
            return querySrv.response.all_true;

        } else {
            ROS_ERROR("KCL: (%s) Failed to call service query_state", ros::this_node::getName().c_str());
        }
    }

    bool PlanDispatcher::goalAchieved() {
        rosplan_knowledge_msgs::GetAttributeService goal;
        if (not get_goals.call(goal)) {
            ROS_ERROR("KCL: (%s) Failed to call service get_goals", ros::this_node::getName().c_str());
            return false;
        }
        if (goal.response.attributes.empty()) return false;
        rosplan_knowledge_msgs::KnowledgeQueryService querySrv;
        querySrv.request.knowledge = goal.response.attributes;
        if (not queryKnowledgeClient.call(querySrv)) {
            ROS_ERROR("KCL: (%s) Failed to call service query_state", ros::this_node::getName().c_str());
            return false;
        }
        return querySrv.response.all_true;
    }

}<commit_msg>Fixed goal check when empty goal<commit_after>//
// Created by Gerard Canal <gcanal@iri.upc.edu> on 11/10/18.
//

#include <rosplan_knowledge_msgs/GetAttributeService.h>
#include "rosplan_planning_system/PlanDispatch/PlanDispatcher.h"

namespace KCL_rosplan {
    PlanDispatcher::PlanDispatcher(ros::NodeHandle& nh): as_(nh, "dispatch_plan_action", false) {
        node_handle = &nh;

        kb_ = "knowledge_base";
        node_handle->getParam("knowledge_base", kb_);

        std::stringstream ss;
        ss << "/" << kb_ << "/query_state";
        queryKnowledgeClient = node_handle->serviceClient<rosplan_knowledge_msgs::KnowledgeQueryService>(ss.str());
        ss.str("");
        ss << "/" << kb_ << "/domain/operator_details";
        queryDomainClient = node_handle->serviceClient<rosplan_knowledge_msgs::GetDomainOperatorDetailsService>(ss.str());
        ss.str("");
        ss << "/" << kb_ << "/state/goals";
        get_goals = node_handle->serviceClient<rosplan_knowledge_msgs::GetAttributeService>(ss.str());
        ss.str("");

        // Register actionlib callbacks
        as_.registerGoalCallback(boost::bind(&KCL_rosplan::PlanDispatcher::dispatchPlanActionlib, this));
        as_.registerPreemptCallback(boost::bind(&KCL_rosplan::PlanDispatcher::cancelDispatch, this));
        as_.start(); // Start the action

        // start the plan parsing services
        service1 = nh.advertiseService("dispatch_plan", &KCL_rosplan::PlanDispatcher::dispatchPlanService, this);
        service2 = nh.advertiseService("cancel_dispatch", &KCL_rosplan::PlanDispatcher::cancelDispatchService, this);
        dispatching = false;

        // Action topics
        action_dispatch_topic = "action_dispatch";
        action_feedback_topic = "action_feedback";
        nh.getParam("action_dispatch_topic", action_dispatch_topic);
        nh.getParam("action_feedback_topic", action_feedback_topic);
        action_dispatch_publisher = node_handle->advertise<rosplan_dispatch_msgs::ActionDispatch>(action_dispatch_topic, 1, true);
        action_feedback_publisher = node_handle->advertise<rosplan_dispatch_msgs::ActionFeedback>(action_feedback_topic, 1, true);
    }

    void PlanDispatcher::publishFeedback(const rosplan_dispatch_msgs::ActionFeedback &fb) {
        if (as_.isActive()) { // Action server is the one dispatching
            rosplan_dispatch_msgs::NonBlockingDispatchFeedback afeedback;
            afeedback.feedback = fb;
            as_.publishFeedback(afeedback);
        }
        // Publish feedback in topic anyway...
        action_feedback_publisher.publish(fb);
    }

    bool PlanDispatcher::cancelDispatch() {
        if (as_.isNewGoalAvailable()) { // Preempt caused by sending of new goal, ignore it
            //ROS_WARN("KCL: (%s) Got a new dispatch request but a plan is already being dispatched!", ros::this_node::getName().c_str());
            // Don't cancel a goal as it is being preempted because of a new goal sent
            return false;
        }
        ROS_INFO("KCL: (%s) Cancel plan command received.", ros::this_node::getName().c_str());
        if (as_.isActive()) as_.setPreempted();
        plan_cancelled = true;
        return true;
    }

    bool PlanDispatcher::cancelDispatchService(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res) {
        return cancelDispatch();
    }

    /*--------------------*/
    /* Dispatch interface */
    /*--------------------*/

    /**
     * plan dispatch service method (1)
     * dispatches plan as a service
     * @returns True iff every action was dispatched and returned success.
     */
    bool PlanDispatcher::dispatchPlanService(rosplan_dispatch_msgs::DispatchService::Request &req, rosplan_dispatch_msgs::DispatchService::Response &res) {
        if (dispatching) return false;
        dispatching = true;
        bool success = dispatchPlan(mission_start_time,ros::WallTime::now().toSec());
        dispatching = false;
        reset();
        res.success = success;
        res.goal_achieved = goalAchieved();
        return true;
    }

    /**
     * plan dispatch action method (2)
     * dispatches plan as a service
     * @returns True iff every action was dispatched and returned success.
     */
    void PlanDispatcher::dispatchPlanActionlib() {
        if (as_.isActive() or dispatching) {
            ROS_WARN("KCL: (%s) Got a new dispatch request but a plan is already being dispatched! It will be ignored", ros::this_node::getName().c_str());
        }
        else {
            as_.acceptNewGoal();
            dispatching = true;
            bool success = dispatchPlan(mission_start_time, ros::WallTime::now().toSec());
            dispatching = false;
            reset();
            rosplan_dispatch_msgs::NonBlockingDispatchResult res;
            res.success = success;
            res.goal_achieved = goalAchieved();
            as_.setSucceeded(res);
        }
    }

    void PlanDispatcher::reset() {
        replan_requested = false;
        dispatch_paused = false;
        plan_cancelled = false;
        action_received.clear();
        action_completed.clear();
        plan_received = false;
        dispatching = false;
    }

    /**
	 *	Returns true of the actions preconditions are true in the current state. Calls the Knowledge Base.
	 */
    bool PlanDispatcher::checkPreconditions(rosplan_dispatch_msgs::ActionDispatch msg) {

        // get domain opertor details
        rosplan_knowledge_msgs::GetDomainOperatorDetailsService srv;
        srv.request.name = msg.name;
        if(!queryDomainClient.call(srv)) {
            ROS_ERROR("KCL: (%s) could not call Knowledge Base for operator details, %s", ros::this_node::getName().c_str(), msg.name.c_str());
            return false;
        }

        // setup service call
        rosplan_knowledge_msgs::DomainOperator op = srv.response.op;
        rosplan_knowledge_msgs::KnowledgeQueryService querySrv;

        // iterate through conditions
        std::vector<rosplan_knowledge_msgs::DomainFormula>::iterator cit = op.at_start_simple_condition.begin();
        for(; cit!=op.at_start_simple_condition.end(); cit++) {

            // create condition
            rosplan_knowledge_msgs::KnowledgeItem condition;
            condition.knowledge_type = rosplan_knowledge_msgs::KnowledgeItem::FACT;
            condition.attribute_name = cit->name;

            // populate parameters
            for(int i=0; i<cit->typed_parameters.size(); i++) {

                // set parameter label to predicate label
                diagnostic_msgs::KeyValue param;
                param.key = cit->typed_parameters[i].key;

                // search for correct operator parameter value
                for(int j=0; j<msg.parameters.size() && j<op.formula.typed_parameters.size(); j++) {
                    if(op.formula.typed_parameters[j].key == cit->typed_parameters[i].key) {
                        param.value = msg.parameters[j].value;
                    }
                }
                condition.values.push_back(param);
            }
            querySrv.request.knowledge.push_back(condition);
        }

        // check conditions in knowledge base
        if (queryKnowledgeClient.call(querySrv)) {

            if(!querySrv.response.all_true) {
                std::vector<rosplan_knowledge_msgs::KnowledgeItem>::iterator kit;
                for(kit=querySrv.response.false_knowledge.begin(); kit != querySrv.response.false_knowledge.end(); kit++)
                    ROS_INFO("KCL: (%s) Precondition not achieved: %s", ros::this_node::getName().c_str(), kit->attribute_name.c_str());
            }
            return querySrv.response.all_true;

        } else {
            ROS_ERROR("KCL: (%s) Failed to call service query_state", ros::this_node::getName().c_str());
        }
    }

    bool PlanDispatcher::goalAchieved() {
        rosplan_knowledge_msgs::GetAttributeService goal;
        if (not get_goals.call(goal)) {
            ROS_ERROR("KCL: (%s) Failed to call service get_goals", ros::this_node::getName().c_str());
            return false;
        }
        if (goal.response.attributes.empty()) return true;
        rosplan_knowledge_msgs::KnowledgeQueryService querySrv;
        querySrv.request.knowledge = goal.response.attributes;
        if (not queryKnowledgeClient.call(querySrv)) {
            ROS_ERROR("KCL: (%s) Failed to call service query_state", ros::this_node::getName().c_str());
            return false;
        }
        return querySrv.response.all_true;
    }

}<|endoftext|>


<commit_before>/* Copyright 2019 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/lite/delegates/gpu/gl/compiler/shader_codegen.h"

#include <algorithm>

#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/gl/compiler/preprocessor.h"
#include "tensorflow/lite/delegates/gpu/gl/compiler/variable_accessor.h"
#include "tensorflow/lite/delegates/gpu/gl/variable.h"

namespace tflite {
namespace gpu {
namespace gl {

ShaderCodegen::ShaderCodegen(const CompilationOptions& options,
                             const GpuInfo& gpu_info)
    : options_(options), gpu_type_(gpu_info.type) {}

Status ShaderCodegen::Build(CompiledNodeAttributes attr,
                            ShaderCode* shader_code) const {
  VariableAccessor variable_accessor(options_.inline_parameters);
  ObjectAccessor objects(gpu_type_ == GpuType::MALI, options_.sampler_textures,
                         &variable_accessor);

  auto add_object = [&](const std::string& name, Object&& object) {
    if (!objects.AddObject(name, std::forward<Object>(object))) {
      return InternalError("There is an object with the same name");
    }
    return OkStatus();
  };

  auto add_uniform_parameter = [&](Variable&& param) {
    if (!variable_accessor.AddUniformParameter(std::forward<Variable>(param))) {
      return InternalError("There is a parameter with the same name");
    }
    return OkStatus();
  };

  for (auto&& param : attr.code.parameters) {
    RETURN_IF_ERROR(add_uniform_parameter(std::move(param)));
  }

  for (auto&& object : attr.code.objects) {
    RETURN_IF_ERROR(add_object(object.first, std::move(object.second)));
  }

  int index = 0;
  for (auto&& input : attr.inputs) {
    RETURN_IF_ERROR(
        add_object(absl::StrCat("input_data_", index++), std::move(input)));
  }
  index = 0;
  for (auto&& output : attr.outputs) {
    RETURN_IF_ERROR(
        add_object(absl::StrCat("output_data_", index++), std::move(output)));
  }

  // TODO(akulik): workload params need to go away and be replaced with
  // output_data_0_w
  RETURN_IF_ERROR(add_uniform_parameter(
      {"workload_x", static_cast<int32_t>(attr.code.workload.x)}));
  RETURN_IF_ERROR(add_uniform_parameter(
      {"workload_y", static_cast<int32_t>(attr.code.workload.y)}));
  RETURN_IF_ERROR(add_uniform_parameter(
      {"workload_z", static_cast<int32_t>(attr.code.workload.z)}));

  std::string source_code = R"(
  ivec3 gid = ivec3(gl_GlobalInvocationID.xyz);
  if (gid.x >= $workload_x$ || gid.y >= $workload_y$ || gid.z >= $workload_z$) {
    return;
  }
)";

  switch (attr.code.input) {
    case IOStructure::ONLY_DEFINITIONS:
      for (int i = 0; i < attr.inputs.size(); ++i) {
        absl::StrAppend(&source_code, "  highp vec4 value_", i,
                        " = vec4(0);\n");
      }
      break;
    case IOStructure::AUTO: {
      for (int i = 0; i < attr.inputs.size(); ++i) {
        absl::StrAppend(&source_code, "  highp vec4 value_", i,
                        " = $input_data_", i, "[gid.x, gid.y, gid.z]$;\n");
      }
      break;
    }
  }

  source_code.append(attr.code.source_code);

  if (attr.code.output == IOStructure::AUTO) {
    for (int i = 0; i < attr.outputs.size(); ++i) {
      absl::StrAppend(&source_code, "  $output_data_", i,
                      "[gid.x, gid.y, gid.z] = value_", i, "$;\n");
    }
  }

  // At this point main function is already generated. Now we need to process
  // object and parameter accessors.

  // process objects first. Object accessor may introduce new uniform
  // parameters that need to be rewritten in the subsequent pass.
  {
    TextPreprocessor preprocessor('$', /*keep_unknown_rewrites=*/true);
    preprocessor.AddRewrite(&objects);
    RETURN_IF_ERROR(preprocessor.Rewrite(source_code, &source_code));
  }

  {
    TextPreprocessor preprocessor('$', /*keep_unknown_rewrites=*/false);
    preprocessor.AddRewrite(&variable_accessor);
    RETURN_IF_ERROR(preprocessor.Rewrite(source_code, &source_code));
  }

  if (options_.inline_parameters) {
    source_code =
        absl::StrCat(variable_accessor.GetConstDeclarations(), source_code);
  }

  std::string declarations = absl::StrCat(
      objects.GetFunctionsDeclarations(), "\n", objects.GetObjectDeclarations(),
      "\n", variable_accessor.GetUniformParameterDeclarations());
  *shader_code = ShaderCode(
      variable_accessor.GetUniformParameters(), objects.GetObjects(),
      attr.code.workload, attr.code.workgroup,
      absl::StrCat("layout(std430) buffer;\nprecision ",
                   options_.allow_precision_loss ? "mediump" : "highp",
                   " float;\n", declarations, "\nvoid main() {\n", source_code,
                   "\n}"),
      attr.node_indices);
  return OkStatus();
}

}  // namespace gl
}  // namespace gpu
}  // namespace tflite
<commit_msg>TFLite GPU OpenGL: Reflect shared variables to shader code generation.<commit_after>/* Copyright 2019 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/lite/delegates/gpu/gl/compiler/shader_codegen.h"

#include <algorithm>

#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/gl/compiler/preprocessor.h"
#include "tensorflow/lite/delegates/gpu/gl/compiler/variable_accessor.h"
#include "tensorflow/lite/delegates/gpu/gl/variable.h"

namespace tflite {
namespace gpu {
namespace gl {

ShaderCodegen::ShaderCodegen(const CompilationOptions& options,
                             const GpuInfo& gpu_info)
    : options_(options), gpu_type_(gpu_info.type) {}

Status ShaderCodegen::Build(CompiledNodeAttributes attr,
                            ShaderCode* shader_code) const {
  VariableAccessor variable_accessor(options_.inline_parameters);
  ObjectAccessor object_accessor(gpu_type_ == GpuType::MALI,
                                 options_.sampler_textures, &variable_accessor);

  const auto add_object = [&](const std::string& name, Object&& object) {
    if (!object_accessor.AddObject(name, std::forward<Object>(object))) {
      return AlreadyExistsError(absl::StrCat("Object \"", name, "\""));
    }
    return OkStatus();
  };

  const auto add_uniform_parameter = [&](Variable&& variable) {
    const std::string name = variable.name;
    if (!variable_accessor.AddUniformParameter(std::move(variable))) {
      return AlreadyExistsError(
          absl::StrCat("Uniform parameter \"", name, "\""));
    }
    return OkStatus();
  };

  for (auto&& object : attr.code.objects) {
    RETURN_IF_ERROR(add_object(object.first, std::move(object.second)));
  }

  for (auto&& variable : attr.code.shared_variables) {
    const std::string name = variable.name;
    if (!variable_accessor.AddSharedVariable(std::move(variable))) {
      return AlreadyExistsError(absl::StrCat("Shared variable \"", name, "\""));
    }
  }

  for (auto&& variable : attr.code.parameters) {
    RETURN_IF_ERROR(add_uniform_parameter(std::move(variable)));
  }

  int index = 0;
  for (auto&& input : attr.inputs) {
    RETURN_IF_ERROR(
        add_object(absl::StrCat("input_data_", index++), std::move(input)));
  }
  index = 0;
  for (auto&& output : attr.outputs) {
    RETURN_IF_ERROR(
        add_object(absl::StrCat("output_data_", index++), std::move(output)));
  }

  // TODO(akulik): workload params need to go away and be replaced with
  // output_data_0_w
  RETURN_IF_ERROR(add_uniform_parameter(
      {"workload_x", static_cast<int32_t>(attr.code.workload.x)}));
  RETURN_IF_ERROR(add_uniform_parameter(
      {"workload_y", static_cast<int32_t>(attr.code.workload.y)}));
  RETURN_IF_ERROR(add_uniform_parameter(
      {"workload_z", static_cast<int32_t>(attr.code.workload.z)}));

  std::string main_source_code = R"(
  ivec3 gid = ivec3(gl_GlobalInvocationID.xyz);
  if (gid.x >= $workload_x$ || gid.y >= $workload_y$ || gid.z >= $workload_z$) {
    return;
  }
)";

  switch (attr.code.input) {
    case IOStructure::ONLY_DEFINITIONS:
      for (int i = 0; i < attr.inputs.size(); ++i) {
        absl::StrAppend(&main_source_code, "  highp vec4 value_", i,
                        " = vec4(0);\n");
      }
      break;
    case IOStructure::AUTO: {
      for (int i = 0; i < attr.inputs.size(); ++i) {
        absl::StrAppend(&main_source_code, "  highp vec4 value_", i,
                        " = $input_data_", i, "[gid.x, gid.y, gid.z]$;\n");
      }
      break;
    }
  }

  main_source_code.append(attr.code.source_code);

  if (attr.code.output == IOStructure::AUTO) {
    for (int i = 0; i < attr.outputs.size(); ++i) {
      absl::StrAppend(&main_source_code, "  $output_data_", i,
                      "[gid.x, gid.y, gid.z] = value_", i, "$;\n");
    }
  }

  // At this point main function is already generated. Now we need to process
  // object and variable accessors.

  // process objects first. Object accessor may introduce new uniform
  // parameters that need to be rewritten in the subsequent pass.
  {
    TextPreprocessor preprocessor('$', /*keep_unknown_rewrites=*/true);
    preprocessor.AddRewrite(&object_accessor);
    RETURN_IF_ERROR(preprocessor.Rewrite(main_source_code, &main_source_code));
  }

  {
    TextPreprocessor preprocessor('$', /*keep_unknown_rewrites=*/false);
    preprocessor.AddRewrite(&variable_accessor);
    RETURN_IF_ERROR(preprocessor.Rewrite(main_source_code, &main_source_code));
  }

  if (options_.inline_parameters) {
    main_source_code = absl::StrCat(variable_accessor.GetConstDeclarations(),
                                    main_source_code);
  }

  // partial_source_code is only missing the following which is added later:
  // #version 310 es
  // layout(local_size_x = ..., local_size_y = ..., local_size_z = ...) in;
  const char* precision = options_.allow_precision_loss ? "mediump" : "highp";
  const std::string partial_source_code = absl::StrCat(
      "layout(std430) buffer;\n",                                 //
      "precision ", precision, " float;\n",                       //
      object_accessor.GetFunctionsDeclarations(), "\n",           //
      object_accessor.GetObjectDeclarations(), "\n",              //
      variable_accessor.GetSharedVariableDeclarations(), "\n",    //
      variable_accessor.GetUniformParameterDeclarations(), "\n",  //
      "void main() {\n",                                          //
      main_source_code,                                           //
      "}");
  *shader_code =
      ShaderCode(variable_accessor.GetUniformParameters(),
                 object_accessor.GetObjects(), attr.code.workload,
                 attr.code.workgroup, partial_source_code, attr.node_indices);
  return OkStatus();
}

}  // namespace gl
}  // namespace gpu
}  // namespace tflite
<|endoftext|>


<commit_before>/**
 * \file
 * \brief FpuSignalTestCase class implementation for ARMv7-M (Cortex-M3 / Cortex-M4)
 *
 * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "ARMv7-M-FpuSignalTestCase.hpp"

#include "distortos/chip/CMSIS-proxy.h"

#if __FPU_PRESENT == 1 && __FPU_USED == 1

#include "checkFpuRegisters.hpp"
#include "setFpuRegisters.hpp"

#include "distortos/DynamicThread.hpp"
#include "distortos/StaticSoftwareTimer.hpp"
#include "distortos/statistics.hpp"
#include "distortos/ThisThread.hpp"
#include "distortos/ThisThread-Signals.hpp"

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

namespace distortos
{

namespace test
{

#if __FPU_PRESENT == 1 && __FPU_USED == 1

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// values used in single test stage
struct Stage
{
	/// value written to all FPU registers in main thread, don't use FPU in main thread if 0
	uint32_t threadValue;

	/// value written to "lower" FPU registers in "sender" before queuing of signal, skip this step if 0
	uint32_t senderValueBefore;

	/// value written to "lower" FPU registers in "sender" after queuing of signal, skip this step if 0
	uint32_t senderValueAfter;

	/// signal value, written to "lower" FPU registers in handler, don't use FPU in handler if 0
	int signalValue;
};

/// description of single test phase
struct Phase
{
	/// type of "sender" function
	using Sender = int(Thread&, const Stage&);

	/// reference to "sender" function
	Sender& sender;

	/// expected number of context switches for single stage executed in this phase
	decltype(statistics::getContextSwitchCount()) contextSwitchCount;
};

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// tested signal number
constexpr uint8_t testSignalNumber {16};

/// size of stack for test thread, bytes
constexpr size_t testThreadStackSize {512};

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Disables FPU context for current thread by clearing FPCA bit in CONTROL register.
 */

void disableFpuContext()
{
	const auto control = __get_CONTROL();
	__set_CONTROL(control & ~CONTROL_FPCA_Msk);
}

/**
 * \brief Signal handler
 *
 * Sets "lower" FPU registers (s0-s15 and FPSCR) using the value queued with signal, unless this value is 0.
 *
 * \param [in] signalInformation is a reference to received SignalInformation object
 */

void handler(const SignalInformation& signalInformation)
{
	const auto value = signalInformation.getValue().sival_int;
	if (value == 0)
		return;

	setFpuRegisters(value, false);
}

/**
 * \brief Tests whether FPU context is active for current thread.
 *
 * \return true if FPU context is active for current thread (FPCA bit in CONTROL register is set), false otherwise
 */

bool isFpuContextActive()
{
	const auto control = __get_CONTROL();
	return (control & CONTROL_FPCA_Msk) != 0;
}

/**
 * \brief Test wrapper for Thread::queueSignal() that also modifies FPU registers.
 *
 * \param [in] thread is a reference to thread to which the signal will be queued
 * \param [in] stage is a reference to test stage
 * \param [in] full is the \a full argument passed to setFpuRegisters()
 * \param [in] sharedRet is a reference to int variable which will be written with 0 on success, error code otherwise
 */

void queueSignalWrapper(Thread& thread, const Stage& stage, const bool full, int& sharedRet)
{
	if (stage.senderValueBefore != 0)	// should FPU be used at the beginning of "sender"?
		setFpuRegisters(stage.senderValueBefore, full);

	sharedRet = thread.queueSignal(testSignalNumber, sigval{stage.signalValue});

	if (stage.senderValueAfter != 0)	// should FPU be used at th"sender""sender"?
		setFpuRegisters(stage.senderValueAfter, full);
}

/**
 * \brief Queues signal from interrupt (via software timer) to current thread.
 *
 * \param [in] thread is a reference to thread to which the signal will be queued
 * \param [in] stage is a reference to test stage
 *
 * \return 0 on success, error code otherwise:
 * - error codes returned by Thread::queueSignal();
 */

int queueSignalFromInterrupt(Thread& thread, const Stage& stage)
{
	int sharedRet {EINVAL};
	auto softwareTimer = makeStaticSoftwareTimer(queueSignalWrapper, std::ref(thread), std::ref(stage), false,
			std::ref(sharedRet));

	softwareTimer.start(TickClock::duration{});
	while (softwareTimer.isRunning() == true);

	return sharedRet;
}

/**
 * \brief Queues signal from thread to non-current thread.
 *
 * \param [in] thread is a reference to thread to which the signal will be queued
 * \param [in] stage is a reference to test stage
 *
 * \return 0 on success, error code otherwise:
 * - error codes returned by Thread::queueSignal();
 */

int queueSignalFromThread(Thread& thread, const Stage& stage)
{
	constexpr decltype(FpuSignalTestCase::getTestCasePriority()) highPriority
	{
			FpuSignalTestCase::getTestCasePriority() + 1
	};

	int sharedRet {EINVAL};
	auto testThread = makeAndStartDynamicThread({testThreadStackSize, highPriority}, queueSignalWrapper,
			std::ref(thread), std::ref(stage), true, std::ref(sharedRet));
	testThread.join();

	return sharedRet;
}

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// test phasese
const Phase phases[]
{
		{queueSignalFromInterrupt, 0},
		{queueSignalFromThread, 2},
};

/// test stages
const Stage stages[]
{
		{0, 0, 0, 0},								// don't use FPU in all contexts
		{0, 0, 0, 0x0b39fa96},						// in handler
		{0, 0, 0x8606151d, 0},						// in "sender" (at the end)
		{0, 0, 0x8bdd3b5e, 0x7d21d1c6},				// in "sender" (at the end) and in handler
		{0, 0x2f884196, 0, 0},						// in "sender" (at the beginning)
		{0, 0x0b0bbc86, 0, 0x0e43811b},				// in "sender" (at the beginning) and in handler
		{0, 0xb72b2917, 0x8c27baa7, 0},				// in "sender"
		{0, 0xa83b80c2, 0xd2b7dd4d, 0x626ca399},	// in "sender" and in handler
		{0xb4e40525, 0, 0, 0},						// in main thread
		{0x772bdf91, 0, 0, 0x0bb325b7},				// in main thread and in handler
		{0x8a19625e, 0, 0x32378f7b, 0},				// in main thread and in "sender" (at the end)
		{0xd17a21db, 0, 0xaa807a91, 0x03caf264},	// in main thread, in "sender" (at the end) and in handler
		{0xe4b44073, 0xa88c0cf5, 0, 0},				// in main thread and in "sender" (at the beginning)
		{0xb94c722b, 0x5f8ca773, 0, 0x288301cf},	// in main thread, in "sender" (at the beginning) and in handler
		{0x347ecfc5, 0xcb4a3584, 0x5a8bf219, 0},	// in main thread and in "sender"
		{0x788ed92e, 0x4b0ddff9, 0x73776a21, 0x48fb1969},	// in all contexts
};

}	// namespace

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

/*---------------------------------------------------------------------------------------------------------------------+
| protected functions
+---------------------------------------------------------------------------------------------------------------------*/

bool FpuSignalTestCase::finalize() const
{
#if __FPU_PRESENT == 1 && __FPU_USED == 1

	disableFpuContext();

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	return SignalsTestCaseCommon::finalize();
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool FpuSignalTestCase::run_() const
{
#if __FPU_PRESENT == 1 && __FPU_USED == 1

	const auto contextSwitchCount = statistics::getContextSwitchCount();
	auto expectedContextSwitchCount = decltype(contextSwitchCount){};

	{
		int ret;
		std::tie(ret, std::ignore) = ThisThread::Signals::setSignalAction(testSignalNumber,
				SignalAction{handler, SignalSet{SignalSet::empty}});
		if (ret != 0)
			return false;
	}

	auto& currentThread = ThisThread::get();

	for (auto& phase : phases)
		for (auto& stage : stages)
		{
			disableFpuContext();

			decltype(setFpuRegisters({}, {})) fpscr {};
			if (stage.threadValue != 0)	// should FPU be used in main thread?
				fpscr = setFpuRegisters(stage.threadValue, true);

			if (phase.sender(currentThread, stage) != 0)
				return false;

			// FPU context may be active only if FPU was used in main thread
			if (isFpuContextActive() != (stage.threadValue != 0))
				return false;

			if (stage.threadValue != 0)	// was FPU used in main thread?
				if (checkFpuRegisters(stage.threadValue, fpscr) == false)
					return false;

			expectedContextSwitchCount += phase.contextSwitchCount;
			if (statistics::getContextSwitchCount() - contextSwitchCount != expectedContextSwitchCount)
				return false;
		}

	if (statistics::getContextSwitchCount() - contextSwitchCount != expectedContextSwitchCount)
		return false;

	return true;

#else

	return true;

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
}

}	// namespace test

}	// namespace distortos
<commit_msg>test: include <cerrno> in ARMv7-M-FpuSignalTestCase.cpp<commit_after>/**
 * \file
 * \brief FpuSignalTestCase class implementation for ARMv7-M (Cortex-M3 / Cortex-M4)
 *
 * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "ARMv7-M-FpuSignalTestCase.hpp"

#include "distortos/chip/CMSIS-proxy.h"

#if __FPU_PRESENT == 1 && __FPU_USED == 1

#include "checkFpuRegisters.hpp"
#include "setFpuRegisters.hpp"

#include "distortos/DynamicThread.hpp"
#include "distortos/StaticSoftwareTimer.hpp"
#include "distortos/statistics.hpp"
#include "distortos/ThisThread.hpp"
#include "distortos/ThisThread-Signals.hpp"

#include <cerrno>

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

namespace distortos
{

namespace test
{

#if __FPU_PRESENT == 1 && __FPU_USED == 1

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// values used in single test stage
struct Stage
{
	/// value written to all FPU registers in main thread, don't use FPU in main thread if 0
	uint32_t threadValue;

	/// value written to "lower" FPU registers in "sender" before queuing of signal, skip this step if 0
	uint32_t senderValueBefore;

	/// value written to "lower" FPU registers in "sender" after queuing of signal, skip this step if 0
	uint32_t senderValueAfter;

	/// signal value, written to "lower" FPU registers in handler, don't use FPU in handler if 0
	int signalValue;
};

/// description of single test phase
struct Phase
{
	/// type of "sender" function
	using Sender = int(Thread&, const Stage&);

	/// reference to "sender" function
	Sender& sender;

	/// expected number of context switches for single stage executed in this phase
	decltype(statistics::getContextSwitchCount()) contextSwitchCount;
};

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// tested signal number
constexpr uint8_t testSignalNumber {16};

/// size of stack for test thread, bytes
constexpr size_t testThreadStackSize {512};

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Disables FPU context for current thread by clearing FPCA bit in CONTROL register.
 */

void disableFpuContext()
{
	const auto control = __get_CONTROL();
	__set_CONTROL(control & ~CONTROL_FPCA_Msk);
}

/**
 * \brief Signal handler
 *
 * Sets "lower" FPU registers (s0-s15 and FPSCR) using the value queued with signal, unless this value is 0.
 *
 * \param [in] signalInformation is a reference to received SignalInformation object
 */

void handler(const SignalInformation& signalInformation)
{
	const auto value = signalInformation.getValue().sival_int;
	if (value == 0)
		return;

	setFpuRegisters(value, false);
}

/**
 * \brief Tests whether FPU context is active for current thread.
 *
 * \return true if FPU context is active for current thread (FPCA bit in CONTROL register is set), false otherwise
 */

bool isFpuContextActive()
{
	const auto control = __get_CONTROL();
	return (control & CONTROL_FPCA_Msk) != 0;
}

/**
 * \brief Test wrapper for Thread::queueSignal() that also modifies FPU registers.
 *
 * \param [in] thread is a reference to thread to which the signal will be queued
 * \param [in] stage is a reference to test stage
 * \param [in] full is the \a full argument passed to setFpuRegisters()
 * \param [in] sharedRet is a reference to int variable which will be written with 0 on success, error code otherwise
 */

void queueSignalWrapper(Thread& thread, const Stage& stage, const bool full, int& sharedRet)
{
	if (stage.senderValueBefore != 0)	// should FPU be used at the beginning of "sender"?
		setFpuRegisters(stage.senderValueBefore, full);

	sharedRet = thread.queueSignal(testSignalNumber, sigval{stage.signalValue});

	if (stage.senderValueAfter != 0)	// should FPU be used at th"sender""sender"?
		setFpuRegisters(stage.senderValueAfter, full);
}

/**
 * \brief Queues signal from interrupt (via software timer) to current thread.
 *
 * \param [in] thread is a reference to thread to which the signal will be queued
 * \param [in] stage is a reference to test stage
 *
 * \return 0 on success, error code otherwise:
 * - error codes returned by Thread::queueSignal();
 */

int queueSignalFromInterrupt(Thread& thread, const Stage& stage)
{
	int sharedRet {EINVAL};
	auto softwareTimer = makeStaticSoftwareTimer(queueSignalWrapper, std::ref(thread), std::ref(stage), false,
			std::ref(sharedRet));

	softwareTimer.start(TickClock::duration{});
	while (softwareTimer.isRunning() == true);

	return sharedRet;
}

/**
 * \brief Queues signal from thread to non-current thread.
 *
 * \param [in] thread is a reference to thread to which the signal will be queued
 * \param [in] stage is a reference to test stage
 *
 * \return 0 on success, error code otherwise:
 * - error codes returned by Thread::queueSignal();
 */

int queueSignalFromThread(Thread& thread, const Stage& stage)
{
	constexpr decltype(FpuSignalTestCase::getTestCasePriority()) highPriority
	{
			FpuSignalTestCase::getTestCasePriority() + 1
	};

	int sharedRet {EINVAL};
	auto testThread = makeAndStartDynamicThread({testThreadStackSize, highPriority}, queueSignalWrapper,
			std::ref(thread), std::ref(stage), true, std::ref(sharedRet));
	testThread.join();

	return sharedRet;
}

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// test phasese
const Phase phases[]
{
		{queueSignalFromInterrupt, 0},
		{queueSignalFromThread, 2},
};

/// test stages
const Stage stages[]
{
		{0, 0, 0, 0},								// don't use FPU in all contexts
		{0, 0, 0, 0x0b39fa96},						// in handler
		{0, 0, 0x8606151d, 0},						// in "sender" (at the end)
		{0, 0, 0x8bdd3b5e, 0x7d21d1c6},				// in "sender" (at the end) and in handler
		{0, 0x2f884196, 0, 0},						// in "sender" (at the beginning)
		{0, 0x0b0bbc86, 0, 0x0e43811b},				// in "sender" (at the beginning) and in handler
		{0, 0xb72b2917, 0x8c27baa7, 0},				// in "sender"
		{0, 0xa83b80c2, 0xd2b7dd4d, 0x626ca399},	// in "sender" and in handler
		{0xb4e40525, 0, 0, 0},						// in main thread
		{0x772bdf91, 0, 0, 0x0bb325b7},				// in main thread and in handler
		{0x8a19625e, 0, 0x32378f7b, 0},				// in main thread and in "sender" (at the end)
		{0xd17a21db, 0, 0xaa807a91, 0x03caf264},	// in main thread, in "sender" (at the end) and in handler
		{0xe4b44073, 0xa88c0cf5, 0, 0},				// in main thread and in "sender" (at the beginning)
		{0xb94c722b, 0x5f8ca773, 0, 0x288301cf},	// in main thread, in "sender" (at the beginning) and in handler
		{0x347ecfc5, 0xcb4a3584, 0x5a8bf219, 0},	// in main thread and in "sender"
		{0x788ed92e, 0x4b0ddff9, 0x73776a21, 0x48fb1969},	// in all contexts
};

}	// namespace

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

/*---------------------------------------------------------------------------------------------------------------------+
| protected functions
+---------------------------------------------------------------------------------------------------------------------*/

bool FpuSignalTestCase::finalize() const
{
#if __FPU_PRESENT == 1 && __FPU_USED == 1

	disableFpuContext();

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	return SignalsTestCaseCommon::finalize();
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool FpuSignalTestCase::run_() const
{
#if __FPU_PRESENT == 1 && __FPU_USED == 1

	const auto contextSwitchCount = statistics::getContextSwitchCount();
	auto expectedContextSwitchCount = decltype(contextSwitchCount){};

	{
		int ret;
		std::tie(ret, std::ignore) = ThisThread::Signals::setSignalAction(testSignalNumber,
				SignalAction{handler, SignalSet{SignalSet::empty}});
		if (ret != 0)
			return false;
	}

	auto& currentThread = ThisThread::get();

	for (auto& phase : phases)
		for (auto& stage : stages)
		{
			disableFpuContext();

			decltype(setFpuRegisters({}, {})) fpscr {};
			if (stage.threadValue != 0)	// should FPU be used in main thread?
				fpscr = setFpuRegisters(stage.threadValue, true);

			if (phase.sender(currentThread, stage) != 0)
				return false;

			// FPU context may be active only if FPU was used in main thread
			if (isFpuContextActive() != (stage.threadValue != 0))
				return false;

			if (stage.threadValue != 0)	// was FPU used in main thread?
				if (checkFpuRegisters(stage.threadValue, fpscr) == false)
					return false;

			expectedContextSwitchCount += phase.contextSwitchCount;
			if (statistics::getContextSwitchCount() - contextSwitchCount != expectedContextSwitchCount)
				return false;
		}

	if (statistics::getContextSwitchCount() - contextSwitchCount != expectedContextSwitchCount)
		return false;

	return true;

#else

	return true;

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
}

}	// namespace test

}	// namespace distortos
<|endoftext|>


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

#include "test/pc/e2e/stats_based_network_quality_metrics_reporter.h"

#include <cstdint>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "api/scoped_refptr.h"
#include "api/stats/rtc_stats.h"
#include "api/stats/rtcstats_objects.h"
#include "api/test/network_emulation/network_emulation_interfaces.h"
#include "api/test/network_emulation_manager.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "rtc_base/event.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/synchronization/mutex.h"
#include "system_wrappers/include/field_trial.h"
#include "test/testsupport/perf_test.h"

namespace webrtc {
namespace webrtc_pc_e2e {
namespace {

constexpr int kStatsWaitTimeoutMs = 1000;

// Field trial which controls whether to report standard-compliant bytes
// sent/received per stream.  If enabled, padding and headers are not included
// in bytes sent or received.
constexpr char kUseStandardBytesStats[] = "WebRTC-UseStandardBytesStats";

std::unique_ptr<EmulatedNetworkStats> PopulateStats(
    std::vector<EmulatedEndpoint*> endpoints,
    NetworkEmulationManager* network_emulation) {
  rtc::Event stats_loaded;
  std::unique_ptr<EmulatedNetworkStats> stats;
  network_emulation->GetStats(endpoints,
                              [&](std::unique_ptr<EmulatedNetworkStats> s) {
                                stats = std::move(s);
                                stats_loaded.Set();
                              });
  bool stats_received = stats_loaded.Wait(kStatsWaitTimeoutMs);
  RTC_CHECK(stats_received);
  return stats;
}

std::map<rtc::IPAddress, std::string> PopulateIpToPeer(
    const std::map<std::string, std::vector<EmulatedEndpoint*>>&
        peer_endpoints) {
  std::map<rtc::IPAddress, std::string> out;
  for (const auto& entry : peer_endpoints) {
    for (const EmulatedEndpoint* const endpoint : entry.second) {
      out.emplace(endpoint->GetPeerLocalAddress(), entry.first);
    }
  }
  return out;
}

}  // namespace

StatsBasedNetworkQualityMetricsReporter::NetworkLayerStatsCollector::
    NetworkLayerStatsCollector(
        std::map<std::string, std::vector<EmulatedEndpoint*>> peer_endpoints,
        NetworkEmulationManager* network_emulation)
    : peer_endpoints_(std::move(peer_endpoints)),
      ip_to_peer_(PopulateIpToPeer(peer_endpoints_)),
      network_emulation_(network_emulation) {}

void StatsBasedNetworkQualityMetricsReporter::NetworkLayerStatsCollector::
    Start() {
  // Check that network stats are clean before test execution.
  for (const auto& entry : peer_endpoints_) {
    std::unique_ptr<EmulatedNetworkStats> stats =
        PopulateStats(entry.second, network_emulation_);
    RTC_CHECK_EQ(stats->PacketsSent(), 0);
    RTC_CHECK_EQ(stats->PacketsReceived(), 0);
  }
}

std::map<std::string,
         StatsBasedNetworkQualityMetricsReporter::NetworkLayerStats>
StatsBasedNetworkQualityMetricsReporter::NetworkLayerStatsCollector::
    GetStats() {
  std::map<std::string, NetworkLayerStats> peer_to_stats;
  std::map<std::string, std::vector<std::string>> sender_to_receivers;
  for (const auto& entry : peer_endpoints_) {
    NetworkLayerStats stats;
    stats.stats = PopulateStats(entry.second, network_emulation_);
    const std::string& peer_name = entry.first;
    for (const auto& income_stats_entry :
         stats.stats->IncomingStatsPerSource()) {
      const rtc::IPAddress& source_ip = income_stats_entry.first;
      auto it = ip_to_peer_.find(source_ip);
      if (it == ip_to_peer_.end()) {
        // Source IP is unknown for this collector, so will be skipped.
        continue;
      }
      sender_to_receivers[it->second].push_back(peer_name);
    }
    peer_to_stats.emplace(peer_name, std::move(stats));
  }
  for (auto& entry : peer_to_stats) {
    const std::vector<std::string>& receivers =
        sender_to_receivers[entry.first];
    entry.second.receivers =
        std::set<std::string>(receivers.begin(), receivers.end());
  }
  return peer_to_stats;
}

void StatsBasedNetworkQualityMetricsReporter::Start(
    absl::string_view test_case_name,
    const TrackIdStreamInfoMap* reporter_helper) {
  test_case_name_ = std::string(test_case_name);
  collector_.Start();
  start_time_ = clock_->CurrentTime();
}

void StatsBasedNetworkQualityMetricsReporter::OnStatsReports(
    absl::string_view pc_label,
    const rtc::scoped_refptr<const RTCStatsReport>& report) {
  PCStats cur_stats;

  auto inbound_stats = report->GetStatsOfType<RTCInboundRTPStreamStats>();
  for (const auto& stat : inbound_stats) {
    cur_stats.payload_received +=
        DataSize::Bytes(stat->bytes_received.ValueOrDefault(0ul) +
                        stat->header_bytes_received.ValueOrDefault(0ul));
  }

  auto outbound_stats = report->GetStatsOfType<RTCOutboundRTPStreamStats>();
  for (const auto& stat : outbound_stats) {
    cur_stats.payload_sent +=
        DataSize::Bytes(stat->bytes_sent.ValueOrDefault(0ul) +
                        stat->header_bytes_sent.ValueOrDefault(0ul));
  }

  auto candidate_pairs_stats = report->GetStatsOfType<RTCTransportStats>();
  for (const auto& stat : candidate_pairs_stats) {
    cur_stats.total_received +=
        DataSize::Bytes(stat->bytes_received.ValueOrDefault(0ul));
    cur_stats.total_sent +=
        DataSize::Bytes(stat->bytes_sent.ValueOrDefault(0ul));
    cur_stats.packets_received += stat->packets_received.ValueOrDefault(0ul);
    cur_stats.packets_sent += stat->packets_sent.ValueOrDefault(0ul);
  }

  MutexLock lock(&mutex_);
  pc_stats_[std::string(pc_label)] = cur_stats;
}

void StatsBasedNetworkQualityMetricsReporter::StopAndReportResults() {
  Timestamp end_time = clock_->CurrentTime();

  if (!webrtc::field_trial::IsEnabled(kUseStandardBytesStats)) {
    RTC_LOG(LS_ERROR)
        << "Non-standard GetStats; \"payload\" counts include RTP headers";
  }

  std::map<std::string, NetworkLayerStats> stats = collector_.GetStats();
  for (const auto& entry : stats) {
    LogNetworkLayerStats(entry.first, entry.second);
  }
  MutexLock lock(&mutex_);
  for (const auto& pair : pc_stats_) {
    auto it = stats.find(pair.first);
    RTC_CHECK(it != stats.end())
        << "Peer name used for PeerConnection stats collection and peer name "
           "used for endpoints naming doesn't match. No endpoints found for "
           "peer "
        << pair.first;
    const NetworkLayerStats& network_layer_stats = it->second;
    int64_t total_packets_received = 0;
    bool found = false;
    for (const auto& dest_peer : network_layer_stats.receivers) {
      auto pc_stats_it = pc_stats_.find(dest_peer);
      if (pc_stats_it == pc_stats_.end()) {
        continue;
      }
      found = true;
      total_packets_received += pc_stats_it->second.packets_received;
    }
    int64_t packet_loss = -1;
    if (found) {
      packet_loss = pair.second.packets_sent - total_packets_received;
    }
    ReportStats(pair.first, pair.second, network_layer_stats, packet_loss,
                end_time);
  }
}

void StatsBasedNetworkQualityMetricsReporter::ReportStats(
    const std::string& pc_label,
    const PCStats& pc_stats,
    const NetworkLayerStats& network_layer_stats,
    int64_t packet_loss,
    const Timestamp& end_time) {
  ReportResult("bytes_dropped", pc_label,
               network_layer_stats.stats->BytesDropped().bytes(),
               "sizeInBytes");
  ReportResult("packets_dropped", pc_label,
               network_layer_stats.stats->PacketsDropped(), "unitless");

  ReportResult("payload_bytes_received", pc_label,
               pc_stats.payload_received.bytes(), "sizeInBytes");
  ReportResult("payload_bytes_sent", pc_label, pc_stats.payload_sent.bytes(),
               "sizeInBytes");

  ReportResult("bytes_sent", pc_label, pc_stats.total_sent.bytes(),
               "sizeInBytes");
  ReportResult("packets_sent", pc_label, pc_stats.packets_sent, "unitless");
  ReportResult("average_send_rate", pc_label,
               (pc_stats.total_sent / (end_time - start_time_)).bytes_per_sec(),
               "bytesPerSecond");
  ReportResult("bytes_received", pc_label, pc_stats.total_received.bytes(),
               "sizeInBytes");
  ReportResult("packets_received", pc_label, pc_stats.packets_received,
               "unitless");
  ReportResult(
      "average_receive_rate", pc_label,
      (pc_stats.total_received / (end_time - start_time_)).bytes_per_sec(),
      "bytesPerSecond");
  ReportResult("sent_packets_loss", pc_label, packet_loss, "unitless");
}

void StatsBasedNetworkQualityMetricsReporter::ReportResult(
    const std::string& metric_name,
    const std::string& network_label,
    const double value,
    const std::string& unit) const {
  test::PrintResult(metric_name, /*modifier=*/"",
                    GetTestCaseName(network_label), value, unit,
                    /*important=*/false);
}

void StatsBasedNetworkQualityMetricsReporter::ReportResult(
    const std::string& metric_name,
    const std::string& network_label,
    const SamplesStatsCounter& value,
    const std::string& unit) const {
  test::PrintResult(metric_name, /*modifier=*/"",
                    GetTestCaseName(network_label), value, unit,
                    /*important=*/false);
}

std::string StatsBasedNetworkQualityMetricsReporter::GetTestCaseName(
    absl::string_view network_label) const {
  rtc::StringBuilder builder;
  builder << test_case_name_ << "/" << network_label.data();
  return builder.str();
}

void StatsBasedNetworkQualityMetricsReporter::LogNetworkLayerStats(
    const std::string& peer_name,
    const NetworkLayerStats& stats) const {
  DataRate average_send_rate = stats.stats->PacketsSent() >= 2
                                   ? stats.stats->AverageSendRate()
                                   : DataRate::Zero();
  DataRate average_receive_rate = stats.stats->PacketsReceived() >= 2
                                      ? stats.stats->AverageReceiveRate()
                                      : DataRate::Zero();
  rtc::StringBuilder log;
  log << "Raw network layer statistic for [" << peer_name << "]:\n"
      << "Local IPs:\n";
  std::vector<rtc::IPAddress> local_ips = stats.stats->LocalAddresses();
  for (size_t i = 0; i < local_ips.size(); ++i) {
    log << "  " << local_ips[i].ToString() << "\n";
  }
  if (!stats.stats->SentPacketsSizeCounter().IsEmpty()) {
    ReportResult("sent_packets_size", peer_name,
                 stats.stats->SentPacketsSizeCounter(), "sizeInBytes");
  }
  if (!stats.stats->ReceivedPacketsSizeCounter().IsEmpty()) {
    ReportResult("received_packets_size", peer_name,
                 stats.stats->ReceivedPacketsSizeCounter(), "sizeInBytes");
  }
  if (!stats.stats->DroppedPacketsSizeCounter().IsEmpty()) {
    ReportResult("dropped_packets_size", peer_name,
                 stats.stats->DroppedPacketsSizeCounter(), "sizeInBytes");
  }

  log << "Send statistic:\n"
      << "  packets: " << stats.stats->PacketsSent()
      << " bytes: " << stats.stats->BytesSent().bytes()
      << " avg_rate (bytes/sec): " << average_send_rate.bytes_per_sec()
      << " avg_rate (bps): " << average_send_rate.bps() << "\n"
      << "Send statistic per destination:\n";

  for (const auto& entry : stats.stats->OutgoingStatsPerDestination()) {
    DataRate source_average_send_rate = entry.second->PacketsSent() >= 2
                                            ? entry.second->AverageSendRate()
                                            : DataRate::Zero();
    log << "(" << entry.first.ToString() << "):\n"
        << "  packets: " << entry.second->PacketsSent()
        << " bytes: " << entry.second->BytesSent().bytes()
        << " avg_rate (bytes/sec): " << source_average_send_rate.bytes_per_sec()
        << " avg_rate (bps): " << source_average_send_rate.bps() << "\n";
    if (!entry.second->SentPacketsSizeCounter().IsEmpty()) {
      ReportResult("sent_packets_size",
                   peer_name + "/" + entry.first.ToString(),
                   stats.stats->SentPacketsSizeCounter(), "sizeInBytes");
    }
  }

  log << "Receive statistic:\n"
      << "  packets: " << stats.stats->PacketsReceived()
      << " bytes: " << stats.stats->BytesReceived().bytes()
      << " avg_rate (bytes/sec): " << average_receive_rate.bytes_per_sec()
      << " avg_rate (bps): " << average_receive_rate.bps() << "\n"
      << "Receive statistic per source:\n";

  for (const auto& entry : stats.stats->IncomingStatsPerSource()) {
    DataRate source_average_receive_rate =
        entry.second->PacketsReceived() >= 2
            ? entry.second->AverageReceiveRate()
            : DataRate::Zero();
    log << "(" << entry.first.ToString() << "):\n"
        << "  packets: " << entry.second->PacketsReceived()
        << " bytes: " << entry.second->BytesReceived().bytes()
        << " avg_rate (bytes/sec): "
        << source_average_receive_rate.bytes_per_sec()
        << " avg_rate (bps): " << source_average_receive_rate.bps() << "\n";
    if (!entry.second->ReceivedPacketsSizeCounter().IsEmpty()) {
      ReportResult("received_packets_size",
                   peer_name + "/" + entry.first.ToString(),
                   stats.stats->ReceivedPacketsSizeCounter(), "sizeInBytes");
    }
    if (!entry.second->DroppedPacketsSizeCounter().IsEmpty()) {
      ReportResult("dropped_packets_size",
                   peer_name + "/" + entry.first.ToString(),
                   stats.stats->DroppedPacketsSizeCounter(), "sizeInBytes");
    }
  }

  RTC_LOG(INFO) << log.str();
}

}  // namespace webrtc_pc_e2e
}  // namespace webrtc
<commit_msg>Report sent_packets_queue_wait_time_us in PC level framework network debug mode<commit_after>/*
 *  Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "test/pc/e2e/stats_based_network_quality_metrics_reporter.h"

#include <cstdint>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "api/scoped_refptr.h"
#include "api/stats/rtc_stats.h"
#include "api/stats/rtcstats_objects.h"
#include "api/test/network_emulation/network_emulation_interfaces.h"
#include "api/test/network_emulation_manager.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "rtc_base/event.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/synchronization/mutex.h"
#include "system_wrappers/include/field_trial.h"
#include "test/testsupport/perf_test.h"

namespace webrtc {
namespace webrtc_pc_e2e {
namespace {

constexpr int kStatsWaitTimeoutMs = 1000;

// Field trial which controls whether to report standard-compliant bytes
// sent/received per stream.  If enabled, padding and headers are not included
// in bytes sent or received.
constexpr char kUseStandardBytesStats[] = "WebRTC-UseStandardBytesStats";

std::unique_ptr<EmulatedNetworkStats> PopulateStats(
    std::vector<EmulatedEndpoint*> endpoints,
    NetworkEmulationManager* network_emulation) {
  rtc::Event stats_loaded;
  std::unique_ptr<EmulatedNetworkStats> stats;
  network_emulation->GetStats(endpoints,
                              [&](std::unique_ptr<EmulatedNetworkStats> s) {
                                stats = std::move(s);
                                stats_loaded.Set();
                              });
  bool stats_received = stats_loaded.Wait(kStatsWaitTimeoutMs);
  RTC_CHECK(stats_received);
  return stats;
}

std::map<rtc::IPAddress, std::string> PopulateIpToPeer(
    const std::map<std::string, std::vector<EmulatedEndpoint*>>&
        peer_endpoints) {
  std::map<rtc::IPAddress, std::string> out;
  for (const auto& entry : peer_endpoints) {
    for (const EmulatedEndpoint* const endpoint : entry.second) {
      out.emplace(endpoint->GetPeerLocalAddress(), entry.first);
    }
  }
  return out;
}

}  // namespace

StatsBasedNetworkQualityMetricsReporter::NetworkLayerStatsCollector::
    NetworkLayerStatsCollector(
        std::map<std::string, std::vector<EmulatedEndpoint*>> peer_endpoints,
        NetworkEmulationManager* network_emulation)
    : peer_endpoints_(std::move(peer_endpoints)),
      ip_to_peer_(PopulateIpToPeer(peer_endpoints_)),
      network_emulation_(network_emulation) {}

void StatsBasedNetworkQualityMetricsReporter::NetworkLayerStatsCollector::
    Start() {
  // Check that network stats are clean before test execution.
  for (const auto& entry : peer_endpoints_) {
    std::unique_ptr<EmulatedNetworkStats> stats =
        PopulateStats(entry.second, network_emulation_);
    RTC_CHECK_EQ(stats->PacketsSent(), 0);
    RTC_CHECK_EQ(stats->PacketsReceived(), 0);
  }
}

std::map<std::string,
         StatsBasedNetworkQualityMetricsReporter::NetworkLayerStats>
StatsBasedNetworkQualityMetricsReporter::NetworkLayerStatsCollector::
    GetStats() {
  std::map<std::string, NetworkLayerStats> peer_to_stats;
  std::map<std::string, std::vector<std::string>> sender_to_receivers;
  for (const auto& entry : peer_endpoints_) {
    NetworkLayerStats stats;
    stats.stats = PopulateStats(entry.second, network_emulation_);
    const std::string& peer_name = entry.first;
    for (const auto& income_stats_entry :
         stats.stats->IncomingStatsPerSource()) {
      const rtc::IPAddress& source_ip = income_stats_entry.first;
      auto it = ip_to_peer_.find(source_ip);
      if (it == ip_to_peer_.end()) {
        // Source IP is unknown for this collector, so will be skipped.
        continue;
      }
      sender_to_receivers[it->second].push_back(peer_name);
    }
    peer_to_stats.emplace(peer_name, std::move(stats));
  }
  for (auto& entry : peer_to_stats) {
    const std::vector<std::string>& receivers =
        sender_to_receivers[entry.first];
    entry.second.receivers =
        std::set<std::string>(receivers.begin(), receivers.end());
  }
  return peer_to_stats;
}

void StatsBasedNetworkQualityMetricsReporter::Start(
    absl::string_view test_case_name,
    const TrackIdStreamInfoMap* reporter_helper) {
  test_case_name_ = std::string(test_case_name);
  collector_.Start();
  start_time_ = clock_->CurrentTime();
}

void StatsBasedNetworkQualityMetricsReporter::OnStatsReports(
    absl::string_view pc_label,
    const rtc::scoped_refptr<const RTCStatsReport>& report) {
  PCStats cur_stats;

  auto inbound_stats = report->GetStatsOfType<RTCInboundRTPStreamStats>();
  for (const auto& stat : inbound_stats) {
    cur_stats.payload_received +=
        DataSize::Bytes(stat->bytes_received.ValueOrDefault(0ul) +
                        stat->header_bytes_received.ValueOrDefault(0ul));
  }

  auto outbound_stats = report->GetStatsOfType<RTCOutboundRTPStreamStats>();
  for (const auto& stat : outbound_stats) {
    cur_stats.payload_sent +=
        DataSize::Bytes(stat->bytes_sent.ValueOrDefault(0ul) +
                        stat->header_bytes_sent.ValueOrDefault(0ul));
  }

  auto candidate_pairs_stats = report->GetStatsOfType<RTCTransportStats>();
  for (const auto& stat : candidate_pairs_stats) {
    cur_stats.total_received +=
        DataSize::Bytes(stat->bytes_received.ValueOrDefault(0ul));
    cur_stats.total_sent +=
        DataSize::Bytes(stat->bytes_sent.ValueOrDefault(0ul));
    cur_stats.packets_received += stat->packets_received.ValueOrDefault(0ul);
    cur_stats.packets_sent += stat->packets_sent.ValueOrDefault(0ul);
  }

  MutexLock lock(&mutex_);
  pc_stats_[std::string(pc_label)] = cur_stats;
}

void StatsBasedNetworkQualityMetricsReporter::StopAndReportResults() {
  Timestamp end_time = clock_->CurrentTime();

  if (!webrtc::field_trial::IsEnabled(kUseStandardBytesStats)) {
    RTC_LOG(LS_ERROR)
        << "Non-standard GetStats; \"payload\" counts include RTP headers";
  }

  std::map<std::string, NetworkLayerStats> stats = collector_.GetStats();
  for (const auto& entry : stats) {
    LogNetworkLayerStats(entry.first, entry.second);
  }
  MutexLock lock(&mutex_);
  for (const auto& pair : pc_stats_) {
    auto it = stats.find(pair.first);
    RTC_CHECK(it != stats.end())
        << "Peer name used for PeerConnection stats collection and peer name "
           "used for endpoints naming doesn't match. No endpoints found for "
           "peer "
        << pair.first;
    const NetworkLayerStats& network_layer_stats = it->second;
    int64_t total_packets_received = 0;
    bool found = false;
    for (const auto& dest_peer : network_layer_stats.receivers) {
      auto pc_stats_it = pc_stats_.find(dest_peer);
      if (pc_stats_it == pc_stats_.end()) {
        continue;
      }
      found = true;
      total_packets_received += pc_stats_it->second.packets_received;
    }
    int64_t packet_loss = -1;
    if (found) {
      packet_loss = pair.second.packets_sent - total_packets_received;
    }
    ReportStats(pair.first, pair.second, network_layer_stats, packet_loss,
                end_time);
  }
}

void StatsBasedNetworkQualityMetricsReporter::ReportStats(
    const std::string& pc_label,
    const PCStats& pc_stats,
    const NetworkLayerStats& network_layer_stats,
    int64_t packet_loss,
    const Timestamp& end_time) {
  ReportResult("bytes_dropped", pc_label,
               network_layer_stats.stats->BytesDropped().bytes(),
               "sizeInBytes");
  ReportResult("packets_dropped", pc_label,
               network_layer_stats.stats->PacketsDropped(), "unitless");

  ReportResult("payload_bytes_received", pc_label,
               pc_stats.payload_received.bytes(), "sizeInBytes");
  ReportResult("payload_bytes_sent", pc_label, pc_stats.payload_sent.bytes(),
               "sizeInBytes");

  ReportResult("bytes_sent", pc_label, pc_stats.total_sent.bytes(),
               "sizeInBytes");
  ReportResult("packets_sent", pc_label, pc_stats.packets_sent, "unitless");
  ReportResult("average_send_rate", pc_label,
               (pc_stats.total_sent / (end_time - start_time_)).bytes_per_sec(),
               "bytesPerSecond");
  ReportResult("bytes_received", pc_label, pc_stats.total_received.bytes(),
               "sizeInBytes");
  ReportResult("packets_received", pc_label, pc_stats.packets_received,
               "unitless");
  ReportResult(
      "average_receive_rate", pc_label,
      (pc_stats.total_received / (end_time - start_time_)).bytes_per_sec(),
      "bytesPerSecond");
  ReportResult("sent_packets_loss", pc_label, packet_loss, "unitless");
}

void StatsBasedNetworkQualityMetricsReporter::ReportResult(
    const std::string& metric_name,
    const std::string& network_label,
    const double value,
    const std::string& unit) const {
  test::PrintResult(metric_name, /*modifier=*/"",
                    GetTestCaseName(network_label), value, unit,
                    /*important=*/false);
}

void StatsBasedNetworkQualityMetricsReporter::ReportResult(
    const std::string& metric_name,
    const std::string& network_label,
    const SamplesStatsCounter& value,
    const std::string& unit) const {
  test::PrintResult(metric_name, /*modifier=*/"",
                    GetTestCaseName(network_label), value, unit,
                    /*important=*/false);
}

std::string StatsBasedNetworkQualityMetricsReporter::GetTestCaseName(
    absl::string_view network_label) const {
  rtc::StringBuilder builder;
  builder << test_case_name_ << "/" << network_label.data();
  return builder.str();
}

void StatsBasedNetworkQualityMetricsReporter::LogNetworkLayerStats(
    const std::string& peer_name,
    const NetworkLayerStats& stats) const {
  DataRate average_send_rate = stats.stats->PacketsSent() >= 2
                                   ? stats.stats->AverageSendRate()
                                   : DataRate::Zero();
  DataRate average_receive_rate = stats.stats->PacketsReceived() >= 2
                                      ? stats.stats->AverageReceiveRate()
                                      : DataRate::Zero();
  rtc::StringBuilder log;
  log << "Raw network layer statistic for [" << peer_name << "]:\n"
      << "Local IPs:\n";
  std::vector<rtc::IPAddress> local_ips = stats.stats->LocalAddresses();
  for (size_t i = 0; i < local_ips.size(); ++i) {
    log << "  " << local_ips[i].ToString() << "\n";
  }
  if (!stats.stats->SentPacketsSizeCounter().IsEmpty()) {
    ReportResult("sent_packets_size", peer_name,
                 stats.stats->SentPacketsSizeCounter(), "sizeInBytes");
  }
  if (!stats.stats->ReceivedPacketsSizeCounter().IsEmpty()) {
    ReportResult("received_packets_size", peer_name,
                 stats.stats->ReceivedPacketsSizeCounter(), "sizeInBytes");
  }
  if (!stats.stats->DroppedPacketsSizeCounter().IsEmpty()) {
    ReportResult("dropped_packets_size", peer_name,
                 stats.stats->DroppedPacketsSizeCounter(), "sizeInBytes");
  }
  if (!stats.stats->SentPacketsQueueWaitTimeUs().IsEmpty()) {
    ReportResult("sent_packets_queue_wait_time_us", peer_name,
                 stats.stats->SentPacketsQueueWaitTimeUs(), "unitless");
  }

  log << "Send statistic:\n"
      << "  packets: " << stats.stats->PacketsSent()
      << " bytes: " << stats.stats->BytesSent().bytes()
      << " avg_rate (bytes/sec): " << average_send_rate.bytes_per_sec()
      << " avg_rate (bps): " << average_send_rate.bps() << "\n"
      << "Send statistic per destination:\n";

  for (const auto& entry : stats.stats->OutgoingStatsPerDestination()) {
    DataRate source_average_send_rate = entry.second->PacketsSent() >= 2
                                            ? entry.second->AverageSendRate()
                                            : DataRate::Zero();
    log << "(" << entry.first.ToString() << "):\n"
        << "  packets: " << entry.second->PacketsSent()
        << " bytes: " << entry.second->BytesSent().bytes()
        << " avg_rate (bytes/sec): " << source_average_send_rate.bytes_per_sec()
        << " avg_rate (bps): " << source_average_send_rate.bps() << "\n";
    if (!entry.second->SentPacketsSizeCounter().IsEmpty()) {
      ReportResult("sent_packets_size",
                   peer_name + "/" + entry.first.ToString(),
                   stats.stats->SentPacketsSizeCounter(), "sizeInBytes");
    }
  }

  log << "Receive statistic:\n"
      << "  packets: " << stats.stats->PacketsReceived()
      << " bytes: " << stats.stats->BytesReceived().bytes()
      << " avg_rate (bytes/sec): " << average_receive_rate.bytes_per_sec()
      << " avg_rate (bps): " << average_receive_rate.bps() << "\n"
      << "Receive statistic per source:\n";

  for (const auto& entry : stats.stats->IncomingStatsPerSource()) {
    DataRate source_average_receive_rate =
        entry.second->PacketsReceived() >= 2
            ? entry.second->AverageReceiveRate()
            : DataRate::Zero();
    log << "(" << entry.first.ToString() << "):\n"
        << "  packets: " << entry.second->PacketsReceived()
        << " bytes: " << entry.second->BytesReceived().bytes()
        << " avg_rate (bytes/sec): "
        << source_average_receive_rate.bytes_per_sec()
        << " avg_rate (bps): " << source_average_receive_rate.bps() << "\n";
    if (!entry.second->ReceivedPacketsSizeCounter().IsEmpty()) {
      ReportResult("received_packets_size",
                   peer_name + "/" + entry.first.ToString(),
                   stats.stats->ReceivedPacketsSizeCounter(), "sizeInBytes");
    }
    if (!entry.second->DroppedPacketsSizeCounter().IsEmpty()) {
      ReportResult("dropped_packets_size",
                   peer_name + "/" + entry.first.ToString(),
                   stats.stats->DroppedPacketsSizeCounter(), "sizeInBytes");
    }
  }

  RTC_LOG(INFO) << log.str();
}

}  // namespace webrtc_pc_e2e
}  // namespace webrtc
<|endoftext|>


<commit_before>#include "density.h"

namespace sirius {

void Density::generate_valence_density_it(K_set& ks__)
{
    Timer t("sirius::Density::generate_valence_density_it", ctx_.comm());

    /* add k-point contribution */
    for (int ikloc = 0; ikloc < (int)ks__.spl_num_kpoints().local_size(); ikloc++)
    {
        int ik = ks__.spl_num_kpoints(ikloc);
        auto kp = ks__[ik];
        occupied_bands_descriptor occupied_bands;

        if (ctx_.fft(0)->parallel())
        {
            occupied_bands = kp->get_occupied_bands_list(kp->blacs_grid().comm_col());
        }
        else
        {
            occupied_bands = kp->get_occupied_bands_list(kp->blacs_grid_slice().comm_col());
        }

        if (!parameters_.full_potential() && kp->num_ranks() > 1)
        {
            linalg<CPU>::gemr2d(kp->wf_size(), occupied_bands.num_occupied_bands(),
                                kp->fv_states(), 0, 0,
                                kp->spinor_wave_functions(0), 0, 0,
                                kp->blacs_grid().context());
        }

        if (ctx_.fft(0)->parallel())
        {
            add_k_point_contribution_it_pfft(ks__[ik], occupied_bands);
        }
        else
        {
            add_k_point_contribution_it(ks__[ik], occupied_bands);
        }
    }

    /* reduce arrays; assume that each rank did it's own fraction of the density */
    if (ctx_.fft(0)->parallel())
    {
        ctx_.mpi_grid().communicator(1 << _dim_k_ | 1 << _dim_col_).allreduce(&rho_->f_it(0), ctx_.fft(0)->local_size());
    }
    else
    {
        ctx_.comm().allreduce(&rho_->f_it(0), ctx_.fft(0)->size());
        for (int j = 0; j < parameters_.num_mag_dims(); j++)
            ctx_.comm().allreduce(&magnetization_[j]->f_it(0), ctx_.fft(0)->size());
    }
}

};
<commit_msg>check one more timer<commit_after>#include "density.h"

namespace sirius {

void Density::generate_valence_density_it(K_set& ks__)
{
    Timer t("sirius::Density::generate_valence_density_it", ctx_.comm());

    /* add k-point contribution */
    for (int ikloc = 0; ikloc < (int)ks__.spl_num_kpoints().local_size(); ikloc++)
    {
        int ik = ks__.spl_num_kpoints(ikloc);
        auto kp = ks__[ik];
        occupied_bands_descriptor occupied_bands;

        if (ctx_.fft(0)->parallel())
        {
            occupied_bands = kp->get_occupied_bands_list(kp->blacs_grid().comm_col());
        }
        else
        {
            occupied_bands = kp->get_occupied_bands_list(kp->blacs_grid_slice().comm_col());
        }

        if (!parameters_.full_potential() && kp->num_ranks() > 1)
        {
            double t0 = -Utils::current_time();
            linalg<CPU>::gemr2d(kp->wf_size(), occupied_bands.num_occupied_bands(),
                                kp->fv_states(), 0, 0,
                                kp->spinor_wave_functions(0), 0, 0,
                                kp->blacs_grid().context());
            t0 += Utils::current_time();
            printf("gemr2d time: %.4f\n", t0);
        }

        if (ctx_.fft(0)->parallel())
        {
            add_k_point_contribution_it_pfft(ks__[ik], occupied_bands);
        }
        else
        {
            add_k_point_contribution_it(ks__[ik], occupied_bands);
        }
    }

    double t0 = -Utils::current_time();
    /* reduce arrays; assume that each rank did it's own fraction of the density */
    if (ctx_.fft(0)->parallel())
    {
        ctx_.mpi_grid().communicator(1 << _dim_k_ | 1 << _dim_col_).allreduce(&rho_->f_it(0), ctx_.fft(0)->local_size());
    }
    else
    {
        ctx_.comm().allreduce(&rho_->f_it(0), ctx_.fft(0)->size());
        for (int j = 0; j < parameters_.num_mag_dims(); j++)
            ctx_.comm().allreduce(&magnetization_[j]->f_it(0), ctx_.fft(0)->size());
    }
    t0 += Utils::current_time();
    printf("reduction time: %.4f\n", t0);
}

};
<|endoftext|>