<commit_before>/*

 [TridentTD] : MANABU's Esp8266 IoT Library
 www.facebook.com/miniNodeMCU

 TridentTD_Ubidots.cpp - A simple client for UBIDOTS

 20/06/2559 Buddism Era  (2016)

Copyright (c) 2016 TridentTD

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 "TridentTD_Ubidots.h"

/**
 * Constructor.
 */
TridentTD_Ubidots::TridentTD_Ubidots(char* token){
    _token = token;
    maxValues       = 10;  //
    currentValue    = 0;
    _variable_count = 30;

    val        = (Value *)malloc(maxValues*sizeof(Value));
    ubidotsVar = (Variable *)malloc(_variable_count*sizeof(Variable));
}

bool TridentTD_Ubidots::loadAll(){
  if(WiFi.status() != WL_CONNECTED){ return -1; }

  float num;
  String response;
  int start_idx;
  int end_idx;

  //------------ Ubidots API ----------------------------
  String url = "http://things.ubidots.com/api/v1.6/variables/";
         url += "?token=";
         url += _token;
  _http.begin(url);

  int httpCode = _http.GET();
  if(httpCode > 0) {
      //DEBUG_PRINT("[UBIDOTS] http code: "); DEBUG_PRINTLN(httpCode);
      // file found at server
      if(httpCode == HTTP_CODE_OK) {
          String payload = _http.getString();
          DEBUG_PRINTLN(payload);

          _http.end();

          _variable_count = payload.substring( 9+ payload.indexOf("\"count\":"), payload.indexOf(", \"next\"")).toInt();

          DEBUG_PRINTLN(_variable_count);

          int start_idx = 12+ payload.indexOf("\"results\":");
          for(byte i =0; i < _variable_count ; i++){
            int end_idx = payload.indexOf("{\"id\":", payload.indexOf("\"derived_expr\":" , start_idx) );
            String variable_json = payload.substring(start_idx,end_idx);

			DEBUG_PRINTLN(variable_json);
            start_idx = end_idx;

            (ubidotsVar+i)->variable_id   = variable_json.substring( 8+ variable_json.indexOf("{\"id\": \""),  variable_json.indexOf("\", \"name\""));
            (ubidotsVar+i)->variable_name = variable_json.substring( 9+ variable_json.indexOf("\"name\": \""), variable_json.indexOf("\", \"icon\""));
            (ubidotsVar+i)->timestamp     = variable_json.substring( 28+ variable_json.indexOf("\"last_value\": {\"timestamp\":"), variable_json.indexOf("\", \"value\"")).toFloat();
            (ubidotsVar+i)->last_value    = variable_json.substring( 9+ variable_json.indexOf( "\"value\":" , variable_json.indexOf("\"last_value\": {\"timestamp\":")), variable_json.indexOf("\", \"context\"")).toFloat();

            DEBUG_PRINTLN((ubidotsVar+i)->variable_id);
            DEBUG_PRINTLN((ubidotsVar+i)->variable_name);
            DEBUG_PRINTLN((ubidotsVar+i)->timestamp);
            DEBUG_PRINTLN((ubidotsVar+i)->last_value);
          }

          return true;
      }
  } else {
      DEBUG_PRINT("[HTTP] GET... failed, error: "); DEBUG_PRINTLN(_http.errorToString(httpCode).c_str());
      _http.end();
      return false;
  }
}

float TridentTD_Ubidots::getValue(String variable_name){
  if(WiFi.status() != WL_CONNECTED){ return -10000; }

  float num;
  String response;
  int start_idx;
  int end_idx;

  String variable_id = "";

  for(byte i =0; i < _variable_count ; i++){
    if((ubidotsVar+i)->variable_name == variable_name ) {
      variable_id = ((ubidotsVar+i)->variable_id);
    }
  }

  if(variable_id == "") {
	Serial.println("[Ubidots] "+variable_name+" Not Found");
	return -10002;
  }

  //------------ Ubidots API ----------------------------
  String url = "http://things.ubidots.com/api/v1.6/variables/";
         url += variable_id;
         url += "/values/?page_size=1&token=";
         url += _token;
  _http.begin(url);

  int httpCode = _http.GET();
  if(httpCode > 0) {
      //DEBUG_PRINT("[UBIDOTS] http code: "); DEBUG_PRINTLN(httpCode);
      // file found at server
      if(httpCode == HTTP_CODE_OK) {
          String payload = _http.getString();
          _http.end();

          //DEBUG_PRINTLN(payload);
          start_idx = 9+ payload.indexOf("\"value\":");
          end_idx   = payload.indexOf(", \"timestamp\"");
          num       = payload.substring(start_idx,end_idx).toFloat();

          //DEBUG_PRINTLN("[UBIDOTS] variable_id : "+ String(variable_id) + ";value : "+payload.substring(start_idx,end_idx));
          return num;
      }
  } else {
      //DEBUG_PRINT("[HTTP] GET... failed, error: "); DEBUG_PRINTLN(_http.errorToString(httpCode).c_str());
      _http.end();
      return -10001;
  }
}

void TridentTD_Ubidots::setValue(String variable_name, double value){
  uint8_t i;
  int8_t variable_idx = -1;

  String variable_id = "";
  for(byte i =0; i < _variable_count ; i++){
    if((ubidotsVar+i)->variable_name == variable_name ) {
      variable_id = ((ubidotsVar+i)->variable_id);
    }
  }
  if(variable_id == "") { return; }

  for(i=0; i<= currentValue; i++){
    if( (val+i)->variable_id == variable_id) { variable_idx = i; }
  }
  if(variable_idx == -1) {
    (val+currentValue)->variable_id = variable_id;
    (val+currentValue)->value_id = value;
    currentValue++;
  }else{
    (val+variable_idx)->variable_id = variable_id;
    (val+variable_idx)->value_id = value;
  }

  if(currentValue>maxValues){
    Serial.println(F("You are sending more than 5 consecutives variables, you just could send 10 variables. Then other variables will be deleted!"));
    currentValue = maxValues;
  }
}

bool TridentTD_Ubidots::sendAll(){
    int i;
    String allvalues;
    String str;
    allvalues = "[";
    for(i=0; i<currentValue;){
        str = String(((val+i)->value_id),5);
        allvalues += "{\"variable\": \"";
        allvalues += String((val + i)->variable_id);
        allvalues += "\", \"value\":";
        allvalues += str;
        allvalues += "}";
        i++;
        if(i<currentValue){
            allvalues += ", ";
        }
    }
    allvalues += "]";
    i = allvalues.length();

    currentValue=1;  // reset ตัวแปร value

    DEBUG_PRINTLN("[UBIDOTS] allvalues : " +allvalues);

  //------------ Ubidots API ----------------------------
  String url = "http://things.ubidots.com/api/v1.6/collections/values/?token=";
         url += _token;
  _http.begin(url);
  _http.addHeader("Content-Type","application/json");

  int httpCode = _http.POST(allvalues);
  if(httpCode > 0) {
      //DEBUG_PRINT("[UBIDOTS] http code: "); DEBUG_PRINTLN(httpCode);
      // file found at server
      if(httpCode == HTTP_CODE_OK) {
          String payload = _http.getString();
          _http.end();

          DEBUG_PRINTLN("[UBIDOTS] return    : " + payload);
          return true;
      }
  } else {
      DEBUG_PRINT("[HTTP] GET... failed, error: "); DEBUG_PRINTLN(_http.errorToString(httpCode).c_str());
      _http.end();
      return -1;
  }

}

bool TridentTD_Ubidots::wificonnect(char* ssid, char* pass){
  WiFi.begin(ssid, pass);

  Serial.println();
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP address: "); Serial.println(WiFi.localIP());
}

String TridentTD_Ubidots::getVersion(){
  return _version;
}
<commit_msg>Update TridentTD_Ubidots.cpp<commit_after>/*

 [TridentTD] : MANABU's Esp8266 IoT Library
 www.facebook.com/miniNodeMCU

 TridentTD_Ubidots.cpp - A simple client for UBIDOTS

 20/06/2559 Buddism Era  (2016)

Copyright (c) 2016 TridentTD

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 "TridentTD_Ubidots.h"

/**
 * Constructor.
 */
TridentTD_Ubidots::TridentTD_Ubidots(char* token){
    _token = token;
    maxValues       = 10;  //
    currentValue    = 0;
    _variable_count = 30;

    val        = (Value *)malloc(maxValues*sizeof(Value));
    ubidotsVar = (Variable *)malloc(_variable_count*sizeof(Variable));
}

bool TridentTD_Ubidots::loadAll(){
  if(WiFi.status() != WL_CONNECTED){ return -1; }

  float num;
  String response;
  int start_idx;
  int end_idx;

  //------------ Ubidots API ----------------------------
  String url = "http://things.ubidots.com/api/v1.6/variables/";
         url += "?token=";
         url += _token;
  _http.begin(url);

  int httpCode = _http.GET();
  if(httpCode > 0) {
      //DEBUG_PRINT("[UBIDOTS] http code: "); DEBUG_PRINTLN(httpCode);
      // file found at server
      if(httpCode == HTTP_CODE_OK) {
          String payload = _http.getString();
          DEBUG_PRINTLN(payload);

          _http.end();

          _variable_count = payload.substring( 9+ payload.indexOf("\"count\":"), payload.indexOf(", \"next\"")).toInt();

          DEBUG_PRINTLN(_variable_count);

          int start_idx = 12+ payload.indexOf("\"results\":");
          for(byte i =0; i < _variable_count ; i++){
            int end_idx = payload.indexOf("{\"id\":", payload.indexOf("\"derived_expr\":" , start_idx) );
            String variable_json = payload.substring(start_idx,end_idx);

			DEBUG_PRINTLN(variable_json);
            start_idx = end_idx;

            (ubidotsVar+i)->variable_id   = variable_json.substring( 8+ variable_json.indexOf("{\"id\": \""),  variable_json.indexOf("\", \"name\""));
            (ubidotsVar+i)->variable_name = variable_json.substring( 9+ variable_json.indexOf("\"name\": \""), variable_json.indexOf("\", \"icon\""));
            (ubidotsVar+i)->timestamp     = variable_json.substring( 28+ variable_json.indexOf("\"last_value\": {\"timestamp\":"), variable_json.indexOf("\", \"value\"")).toFloat();
            (ubidotsVar+i)->last_value    = variable_json.substring( 9+ variable_json.indexOf( "\"value\":" , variable_json.indexOf("\"last_value\": {\"timestamp\":")), variable_json.indexOf("\", \"context\"")).toFloat();

            DEBUG_PRINTLN((ubidotsVar+i)->variable_id);
            DEBUG_PRINTLN((ubidotsVar+i)->variable_name);
            DEBUG_PRINTLN((ubidotsVar+i)->timestamp);
            DEBUG_PRINTLN((ubidotsVar+i)->last_value);
          }

          return true;
      }
  } else {
      DEBUG_PRINT("[HTTP] GET... failed, error: "); DEBUG_PRINTLN(_http.errorToString(httpCode).c_str());
      _http.end();
      return false;
  }
}

float TridentTD_Ubidots::getValue(String variable_name){
  if(WiFi.status() != WL_CONNECTED){ return -10000; }

  float num;
  String response;
  int start_idx;
  int end_idx;

  String variable_id = "";

  for(byte i =0; i < _variable_count ; i++){
    if((ubidotsVar+i)->variable_name == variable_name ) {
      variable_id = ((ubidotsVar+i)->variable_id);
    }
  }

  if(variable_id == "") {
	Serial.println("[Ubidots] "+variable_name+" Not Found");
	return -10002;
  }

  //------------ Ubidots API ----------------------------
  String url = "http://things.ubidots.com/api/v1.6/variables/";
         url += variable_id;
         url += "/values/?page_size=1&token=";
         url += _token;
  _http.begin(url);

  int httpCode = _http.GET();
  if(httpCode > 0) {
      //DEBUG_PRINT("[UBIDOTS] http code: "); DEBUG_PRINTLN(httpCode);
      // file found at server
      if(httpCode == HTTP_CODE_OK) {
          String payload = _http.getString();
          _http.end();

          //DEBUG_PRINTLN(payload);
          start_idx = 9+ payload.indexOf("\"value\":");
          end_idx   = payload.indexOf(", \"timestamp\"");
          num       = payload.substring(start_idx,end_idx).toFloat();

          //DEBUG_PRINTLN("[UBIDOTS] variable_id : "+ String(variable_id) + ";value : "+payload.substring(start_idx,end_idx));
          return num;
      }
  } else {
      //DEBUG_PRINT("[HTTP] GET... failed, error: "); DEBUG_PRINTLN(_http.errorToString(httpCode).c_str());
      _http.end();
      return -10001;
  }
}

void TridentTD_Ubidots::setValue(String variable_name, double value){
  uint8_t i;
  int8_t variable_idx = -1;

  String variable_id = "";
  for(byte i =0; i < _variable_count ; i++){
    if((ubidotsVar+i)->variable_name == variable_name ) {
      variable_id = ((ubidotsVar+i)->variable_id);
    }
  }
  if(variable_id == "") { return; }

  for(i=0; i<= currentValue; i++){
    if( (val+i)->variable_id == variable_id) { variable_idx = i; }
  }
  if(variable_idx == -1) {
    (val+currentValue)->variable_id = variable_id;
    (val+currentValue)->value_id = value;
    currentValue++;
  }else{
    (val+variable_idx)->variable_id = variable_id;
    (val+variable_idx)->value_id = value;
  }

  if(currentValue>maxValues){
    Serial.println(F("You are sending more than 10 consecutives variables, you just could send 10 variables. Then other variables will be deleted!"));
    currentValue = maxValues;
  }
}

bool TridentTD_Ubidots::sendAll(){
    int i;
    String allvalues;
    String str;
    allvalues = "[";
    for(i=0; i<currentValue;){
        str = String(((val+i)->value_id),5);
        allvalues += "{\"variable\": \"";
        allvalues += String((val + i)->variable_id);
        allvalues += "\", \"value\":";
        allvalues += str;
        allvalues += "}";
        i++;
        if(i<currentValue){
            allvalues += ", ";
        }
    }
    allvalues += "]";
    i = allvalues.length();

    currentValue=1;  // reset ตัวแปร value

    DEBUG_PRINTLN("[UBIDOTS] allvalues : " +allvalues);

  //------------ Ubidots API ----------------------------
  String url = "http://things.ubidots.com/api/v1.6/collections/values/?token=";
         url += _token;
  _http.begin(url);
  _http.addHeader("Content-Type","application/json");

  int httpCode = _http.POST(allvalues);
  if(httpCode > 0) {
      //DEBUG_PRINT("[UBIDOTS] http code: "); DEBUG_PRINTLN(httpCode);
      // file found at server
      if(httpCode == HTTP_CODE_OK) {
          String payload = _http.getString();
          _http.end();

          DEBUG_PRINTLN("[UBIDOTS] return    : " + payload);
          return true;
      }
  } else {
      DEBUG_PRINT("[HTTP] GET... failed, error: "); DEBUG_PRINTLN(_http.errorToString(httpCode).c_str());
      _http.end();
      return -1;
  }

}

bool TridentTD_Ubidots::wificonnect(char* ssid, char* pass){
  WiFi.begin(ssid, pass);

  Serial.println();
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP address: "); Serial.println(WiFi.localIP());
}

String TridentTD_Ubidots::getVersion(){
  return _version;
}
<|endoftext|>


<commit_before>//===- AnalysisOrderChecker - Print callbacks called ------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This checker prints callbacks that are called during analysis.
// This is required to ensure that callbacks are fired in order
// and do not duplicate or get lost.
// Feel free to extend this checker with any callback you need to check.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/ExprCXX.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

using namespace clang;
using namespace ento;

namespace {

class AnalysisOrderChecker
    : public Checker<check::PreStmt<CastExpr>,
                     check::PostStmt<CastExpr>,
                     check::PreStmt<ArraySubscriptExpr>,
                     check::PostStmt<ArraySubscriptExpr>,
                     check::PreStmt<CXXNewExpr>,
                     check::PostStmt<CXXNewExpr>,
                     check::PreCall,
                     check::PostCall,
                     check::NewAllocator,
                     check::Bind,
                     check::RegionChanges,
                     check::LiveSymbols> {

  bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const {
    return Opts.getBooleanOption("*", false, this) ||
        Opts.getBooleanOption(CallbackName, false, this);
  }

  bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const {
    AnalyzerOptions &Opts = C.getAnalysisManager().getAnalyzerOptions();
    return isCallbackEnabled(Opts, CallbackName);
  }

  bool isCallbackEnabled(ProgramStateRef State, StringRef CallbackName) const {
    AnalyzerOptions &Opts = State->getStateManager().getOwningEngine()
                                 ->getAnalysisManager().getAnalyzerOptions();
    return isCallbackEnabled(Opts, CallbackName);
  }

public:
  void checkPreStmt(const CastExpr *CE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreStmtCastExpr"))
      llvm::errs() << "PreStmt<CastExpr> (Kind : " << CE->getCastKindName()
                   << ")\n";
  }

  void checkPostStmt(const CastExpr *CE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostStmtCastExpr"))
      llvm::errs() << "PostStmt<CastExpr> (Kind : " << CE->getCastKindName()
                   << ")\n";
  }

  void checkPreStmt(const ArraySubscriptExpr *SubExpr,
                    CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreStmtArraySubscriptExpr"))
      llvm::errs() << "PreStmt<ArraySubscriptExpr>\n";
  }

  void checkPostStmt(const ArraySubscriptExpr *SubExpr,
                     CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostStmtArraySubscriptExpr"))
      llvm::errs() << "PostStmt<ArraySubscriptExpr>\n";
  }

  void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreStmtCXXNewExpr"))
      llvm::errs() << "PreStmt<CXXNewExpr>\n";
  }

  void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostStmtCXXNewExpr"))
      llvm::errs() << "PostStmt<CXXNewExpr>\n";
  }

  void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreCall")) {
      llvm::errs() << "PreCall";
      if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
        llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
      llvm::errs() << '\n';
    }
  }

  void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostCall")) {
      llvm::errs() << "PostCall";
      if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
        llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
      llvm::errs() << '\n';
    }
  }

  void checkNewAllocator(const CXXNewExpr *CNE, SVal Target,
                         CheckerContext &C) const {
    if (isCallbackEnabled(C, "NewAllocator"))
      llvm::errs() << "NewAllocator\n";
  }

  void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
    if (isCallbackEnabled(C, "Bind"))
      llvm::errs() << "Bind\n";
  }

  void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SymReaper) const {
    if (isCallbackEnabled(State, "LiveSymbols"))
      llvm::errs() << "LiveSymbols\n";
  }

  ProgramStateRef
  checkRegionChanges(ProgramStateRef State,
                     const InvalidatedSymbols *Invalidated,
                     ArrayRef<const MemRegion *> ExplicitRegions,
                     ArrayRef<const MemRegion *> Regions,
                     const LocationContext *LCtx, const CallEvent *Call) const {
    if (isCallbackEnabled(State, "RegionChanges"))
      llvm::errs() << "RegionChanges\n";
    return State;
  }
};
} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Registration.
//===----------------------------------------------------------------------===//

void ento::registerAnalysisOrderChecker(CheckerManager &mgr) {
  mgr.registerChecker<AnalysisOrderChecker>();
}
<commit_msg>[analyzer] Add missing pre-post-statement callbacks for OffsetOfExpr.<commit_after>//===- AnalysisOrderChecker - Print callbacks called ------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This checker prints callbacks that are called during analysis.
// This is required to ensure that callbacks are fired in order
// and do not duplicate or get lost.
// Feel free to extend this checker with any callback you need to check.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/ExprCXX.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

using namespace clang;
using namespace ento;

namespace {

class AnalysisOrderChecker
    : public Checker<check::PreStmt<CastExpr>,
                     check::PostStmt<CastExpr>,
                     check::PreStmt<ArraySubscriptExpr>,
                     check::PostStmt<ArraySubscriptExpr>,
                     check::PreStmt<CXXNewExpr>,
                     check::PostStmt<CXXNewExpr>,
                     check::PreStmt<OffsetOfExpr>,
                     check::PostStmt<OffsetOfExpr>,
                     check::PreCall,
                     check::PostCall,
                     check::NewAllocator,
                     check::Bind,
                     check::RegionChanges,
                     check::LiveSymbols> {

  bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const {
    return Opts.getBooleanOption("*", false, this) ||
        Opts.getBooleanOption(CallbackName, false, this);
  }

  bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const {
    AnalyzerOptions &Opts = C.getAnalysisManager().getAnalyzerOptions();
    return isCallbackEnabled(Opts, CallbackName);
  }

  bool isCallbackEnabled(ProgramStateRef State, StringRef CallbackName) const {
    AnalyzerOptions &Opts = State->getStateManager().getOwningEngine()
                                 ->getAnalysisManager().getAnalyzerOptions();
    return isCallbackEnabled(Opts, CallbackName);
  }

public:
  void checkPreStmt(const CastExpr *CE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreStmtCastExpr"))
      llvm::errs() << "PreStmt<CastExpr> (Kind : " << CE->getCastKindName()
                   << ")\n";
  }

  void checkPostStmt(const CastExpr *CE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostStmtCastExpr"))
      llvm::errs() << "PostStmt<CastExpr> (Kind : " << CE->getCastKindName()
                   << ")\n";
  }

  void checkPreStmt(const ArraySubscriptExpr *SubExpr,
                    CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreStmtArraySubscriptExpr"))
      llvm::errs() << "PreStmt<ArraySubscriptExpr>\n";
  }

  void checkPostStmt(const ArraySubscriptExpr *SubExpr,
                     CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostStmtArraySubscriptExpr"))
      llvm::errs() << "PostStmt<ArraySubscriptExpr>\n";
  }

  void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreStmtCXXNewExpr"))
      llvm::errs() << "PreStmt<CXXNewExpr>\n";
  }

  void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostStmtCXXNewExpr"))
      llvm::errs() << "PostStmt<CXXNewExpr>\n";
  }

  void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreStmtOffsetOfExpr"))
      llvm::errs() << "PreStmt<OffsetOfExpr>\n";
  }

  void checkPostStmt(const OffsetOfExpr *OOE, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostStmtOffsetOfExpr"))
      llvm::errs() << "PostStmt<OffsetOfExpr>\n";
  }

  void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PreCall")) {
      llvm::errs() << "PreCall";
      if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
        llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
      llvm::errs() << '\n';
    }
  }

  void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
    if (isCallbackEnabled(C, "PostCall")) {
      llvm::errs() << "PostCall";
      if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
        llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
      llvm::errs() << '\n';
    }
  }

  void checkNewAllocator(const CXXNewExpr *CNE, SVal Target,
                         CheckerContext &C) const {
    if (isCallbackEnabled(C, "NewAllocator"))
      llvm::errs() << "NewAllocator\n";
  }

  void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
    if (isCallbackEnabled(C, "Bind"))
      llvm::errs() << "Bind\n";
  }

  void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SymReaper) const {
    if (isCallbackEnabled(State, "LiveSymbols"))
      llvm::errs() << "LiveSymbols\n";
  }

  ProgramStateRef
  checkRegionChanges(ProgramStateRef State,
                     const InvalidatedSymbols *Invalidated,
                     ArrayRef<const MemRegion *> ExplicitRegions,
                     ArrayRef<const MemRegion *> Regions,
                     const LocationContext *LCtx, const CallEvent *Call) const {
    if (isCallbackEnabled(State, "RegionChanges"))
      llvm::errs() << "RegionChanges\n";
    return State;
  }
};
} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Registration.
//===----------------------------------------------------------------------===//

void ento::registerAnalysisOrderChecker(CheckerManager &mgr) {
  mgr.registerChecker<AnalysisOrderChecker>();
}
<|endoftext|>


<commit_before>//===-- ExternalMethods.cpp - Implement External Methods ------------------===//
//
//  This file contains both code to deal with invoking "external" methods, but
//  also contains code that implements "exported" external methods.
//
//  External methods in LLI are implemented by dlopen'ing the lli executable and
//  using dlsym to look op the methods that we want to invoke.  If a method is
//  found, then the arguments are mangled and passed in to the function call.
//
//===----------------------------------------------------------------------===//

#include "Interpreter.h"
#include "llvm/DerivedTypes.h"
#include <map>
#include <dlfcn.h>
#include <link.h>
#include <math.h>

typedef GenericValue (*ExFunc)(MethodType *, const vector<GenericValue> &);
static map<const Method *, ExFunc> Functions;
static map<string, ExFunc> FuncNames;

static Interpreter *TheInterpreter;

// getCurrentExecutablePath() - Return the directory that the lli executable
// lives in.
//
string Interpreter::getCurrentExecutablePath() const {
  Dl_info Info;
  if (dladdr(&TheInterpreter, &Info) == 0) return "";

  string LinkAddr(Info.dli_fname);
  unsigned SlashPos = LinkAddr.rfind('/');
  if (SlashPos != string::npos)
    LinkAddr.resize(SlashPos);    // Trim the executable name off...

  return LinkAddr;
}

static char getTypeID(const Type *Ty) {
  switch (Ty->getPrimitiveID()) {
  case Type::VoidTyID:    return 'V';
  case Type::BoolTyID:    return 'o';
  case Type::UByteTyID:   return 'B';
  case Type::SByteTyID:   return 'b';
  case Type::UShortTyID:  return 'S';
  case Type::ShortTyID:   return 's';
  case Type::UIntTyID:    return 'I';
  case Type::IntTyID:     return 'i';
  case Type::ULongTyID:   return 'L';
  case Type::LongTyID:    return 'l';
  case Type::FloatTyID:   return 'F';
  case Type::DoubleTyID:  return 'D';
  case Type::PointerTyID: return 'P';
  case Type::MethodTyID:  return 'M';
  case Type::StructTyID:  return 'T';
  case Type::ArrayTyID:   return 'A';
  case Type::OpaqueTyID:  return 'O';
  default: return 'U';
  }
}

static ExFunc lookupMethod(const Method *M) {
  // Function not found, look it up... start by figuring out what the
  // composite function name should be.
  string ExtName = "lle_";
  const MethodType *MT = M->getMethodType();
  for (unsigned i = 0; const Type *Ty = MT->getContainedType(i); ++i)
    ExtName += getTypeID(Ty);
  ExtName += "_" + M->getName();

  //cout << "Tried: '" << ExtName << "'\n";
  ExFunc FnPtr = FuncNames[ExtName];
  if (FnPtr == 0)
    FnPtr = (ExFunc)dlsym(RTLD_DEFAULT, ExtName.c_str());
  if (FnPtr == 0)
    FnPtr = FuncNames["lle_X_"+M->getName()];
  if (FnPtr == 0)  // Try calling a generic function... if it exists...
    FnPtr = (ExFunc)dlsym(RTLD_DEFAULT, ("lle_X_"+M->getName()).c_str());
  if (FnPtr != 0)
    Functions.insert(make_pair(M, FnPtr));  // Cache for later
  return FnPtr;
}

GenericValue Interpreter::callExternalMethod(Method *M,
                                         const vector<GenericValue> &ArgVals) {
  TheInterpreter = this;

  // Do a lookup to see if the method is in our cache... this should just be a
  // defered annotation!
  map<const Method *, ExFunc>::iterator FI = Functions.find(M);
  ExFunc Fn = (FI == Functions.end()) ? lookupMethod(M) : FI->second;
  if (Fn == 0) {
    cout << "Tried to execute an unknown external method: "
	 << M->getType()->getDescription() << " " << M->getName() << endl;
    return GenericValue();
  }

  // TODO: FIXME when types are not const!
  GenericValue Result = Fn(const_cast<MethodType*>(M->getMethodType()),ArgVals);
  return Result;
}

//===----------------------------------------------------------------------===//
//  Methods "exported" to the running application...
//
extern "C" {  // Don't add C++ manglings to llvm mangling :)

// Implement void printstr([ubyte {x N}] *)
GenericValue lle_VP_printstr(MethodType *M, const vector<GenericValue> &ArgVal){
  assert(ArgVal.size() == 1 && "printstr only takes one argument!");
  cout << (char*)ArgVal[0].PointerVal;
  return GenericValue();
}

// Implement 'void print(X)' for every type...
GenericValue lle_X_print(MethodType *M, const vector<GenericValue> &ArgVals) {
  assert(ArgVals.size() == 1 && "generic print only takes one argument!");

  Interpreter::print(M->getParamTypes()[0], ArgVals[0]);
  return GenericValue();
}

// Implement 'void printVal(X)' for every type...
GenericValue lle_X_printVal(MethodType *M, const vector<GenericValue> &ArgVal) {
  assert(ArgVal.size() == 1 && "generic print only takes one argument!");

  // Specialize print([ubyte {x N} ] *) and print(sbyte *)
  if (PointerType *PTy = dyn_cast<PointerType>(M->getParamTypes()[0].get()))
    if (PTy->getValueType() == Type::SByteTy ||
        isa<ArrayType>(PTy->getValueType())) {
      return lle_VP_printstr(M, ArgVal);
    }

  Interpreter::printValue(M->getParamTypes()[0], ArgVal[0]);
  return GenericValue();
}

// Implement 'void printString(X)'
// Argument must be [ubyte {x N} ] * or sbyte *
GenericValue lle_X_printString(MethodType *M, const vector<GenericValue> &ArgVal) {
  assert(ArgVal.size() == 1 && "generic print only takes one argument!");
  return lle_VP_printstr(M, ArgVal);
}

// Implement 'void print<TYPE>(X)' for each primitive type or pointer type
#define PRINT_TYPE_FUNC(TYPENAME,TYPEID) \
  GenericValue lle_X_print##TYPENAME(MethodType *M,\
                                     const vector<GenericValue> &ArgVal) {\
    assert(ArgVal.size() == 1 && "generic print only takes one argument!");\
    assert(M->getParamTypes()[0].get()->getPrimitiveID() == Type::##TYPEID);\
    Interpreter::printValue(M->getParamTypes()[0], ArgVal[0]);\
    return GenericValue();\
  }

PRINT_TYPE_FUNC(SByte,   SByteTyID)
PRINT_TYPE_FUNC(UByte,   UByteTyID)
PRINT_TYPE_FUNC(Short,   ShortTyID)
PRINT_TYPE_FUNC(UShort,  UShortTyID)
PRINT_TYPE_FUNC(Int,     IntTyID)
PRINT_TYPE_FUNC(UInt,    UIntTyID)
PRINT_TYPE_FUNC(Long,    LongTyID)
PRINT_TYPE_FUNC(ULong,   ULongTyID)
PRINT_TYPE_FUNC(Float,   FloatTyID)
PRINT_TYPE_FUNC(Double,  DoubleTyID)
PRINT_TYPE_FUNC(Pointer, PointerTyID)

// void "putchar"(sbyte)
GenericValue lle_Vb_putchar(MethodType *M, const vector<GenericValue> &Args) {
  cout << Args[0].SByteVal;
  return GenericValue();
}

// int "putchar"(int)
GenericValue lle_ii_putchar(MethodType *M, const vector<GenericValue> &Args) {
  cout << ((char)Args[0].IntVal) << flush;
  return Args[0];
}

// void "putchar"(ubyte)
GenericValue lle_VB_putchar(MethodType *M, const vector<GenericValue> &Args) {
  cout << Args[0].SByteVal << flush;
  return Args[0];
}

// void "__main"()
GenericValue lle_V___main(MethodType *M, const vector<GenericValue> &Args) {
  return GenericValue();
}

// void "exit"(int)
GenericValue lle_Vi_exit(MethodType *M, const vector<GenericValue> &Args) {
  TheInterpreter->exitCalled(Args[0]);
  return GenericValue();
}

// void *malloc(uint)
GenericValue lle_PI_malloc(MethodType *M, const vector<GenericValue> &Args) {
  assert(Args.size() == 1 && "Malloc expects one argument!");
  GenericValue GV;
  GV.PointerVal = (uint64_t)malloc(Args[0].UIntVal);
  return GV;
}

// void free(void *)
GenericValue lle_VP_free(MethodType *M, const vector<GenericValue> &Args) {
  free((void*)Args[0].PointerVal);
  return GenericValue();
}

// double pow(double, double)
GenericValue lle_DDD_pow(MethodType *M, const vector<GenericValue> &Args) {
  GenericValue GV;
  GV.DoubleVal = pow(Args[0].DoubleVal, Args[1].DoubleVal);
  return GV;
}

// int printf(sbyte *, ...) - a very rough implementation to make output useful.
GenericValue lle_iP_printf(MethodType *M, const vector<GenericValue> &Args) {
  const char *FmtStr = (const char *)Args[0].PointerVal;
  unsigned ArgNo = 1;

  // printf should return # chars printed.  This is completely incorrect, but
  // close enough for now.
  GenericValue GV; GV.IntVal = strlen(FmtStr);
  while (1) {
    switch (*FmtStr) {
    case 0: return GV;             // Null terminator...
    default:                       // Normal nonspecial character
      cout << *FmtStr++;
      break;
    case '\\': {                   // Handle escape codes
      char Buffer[3];
      Buffer[0] = *FmtStr++;
      Buffer[1] = *FmtStr++;
      Buffer[2] = 0;
      cout << Buffer;
      break;
    }
    case '%': {                    // Handle format specifiers
      bool isLong = false;
      ++FmtStr;
      if (*FmtStr == 'l') {
        isLong = true;
        FmtStr++;
      }

      switch (*FmtStr) {
      case '%': cout << *FmtStr; break;                             // %%
      case 'd':                                                     // %d %i
      case 'i': cout << Args[ArgNo++].IntVal; break;
      case 'u': cout << Args[ArgNo++].UIntVal; break;               // %u
      case 'o': cout << oct << Args[ArgNo++].UIntVal << dec; break; // %o
      case 'x':
      case 'X': cout << hex << Args[ArgNo++].UIntVal << dec; break; // %x %X
      case 'e': case 'E': case 'g': case 'G':                       // %[eEgG]
        cout /*<< std::scientific*/ << Args[ArgNo++].DoubleVal
             /*<< std::fixed*/; break;
      case 'f': cout << Args[ArgNo++].DoubleVal; break;             // %f
      case 'c': cout << Args[ArgNo++].UByteVal; break;              // %c
      case 's': cout << (char*)Args[ArgNo++].PointerVal; break;     // %s
      default:  cout << "<unknown printf code '" << *FmtStr << "'!>";
                ArgNo++; break;
      }
      ++FmtStr;
      break;
    }
    }
  }
}

} // End extern "C"

void Interpreter::initializeExternalMethods() {
  FuncNames["lle_VP_printstr"] = lle_VP_printstr;
  FuncNames["lle_X_print"] = lle_X_print;
  FuncNames["lle_X_printVal"] = lle_X_printVal;
  FuncNames["lle_X_printString"] = lle_X_printString;
  FuncNames["lle_X_printUByte"] = lle_X_printUByte;
  FuncNames["lle_X_printSByte"] = lle_X_printSByte;
  FuncNames["lle_X_printUShort"] = lle_X_printUShort;
  FuncNames["lle_X_printShort"] = lle_X_printShort;
  FuncNames["lle_X_printInt"] = lle_X_printInt;
  FuncNames["lle_X_printUInt"] = lle_X_printUInt;
  FuncNames["lle_X_printLong"] = lle_X_printLong;
  FuncNames["lle_X_printULong"] = lle_X_printULong;
  FuncNames["lle_X_printFloat"] = lle_X_printFloat;
  FuncNames["lle_X_printDouble"] = lle_X_printDouble;
  FuncNames["lle_X_printPointer"] = lle_X_printPointer;
  FuncNames["lle_Vb_putchar"] = lle_Vb_putchar;
  FuncNames["lle_ii_putchar"] = lle_ii_putchar;
  FuncNames["lle_VB_putchar"] = lle_VB_putchar;
  FuncNames["lle_V___main"] = lle_V___main;
  FuncNames["lle_Vi_exit"] = lle_Vi_exit;
  FuncNames["lle_PI_malloc"] = lle_PI_malloc;
  FuncNames["lle_VP_free"] = lle_VP_free;
  FuncNames["lle_DDD_pow"] = lle_DDD_pow;
  FuncNames["lle_iP_printf"] = lle_iP_printf;
}
<commit_msg>Be lenient on types so that programs that are not very typesafe will work<commit_after>//===-- ExternalMethods.cpp - Implement External Methods ------------------===//
//
//  This file contains both code to deal with invoking "external" methods, but
//  also contains code that implements "exported" external methods.
//
//  External methods in LLI are implemented by dlopen'ing the lli executable and
//  using dlsym to look op the methods that we want to invoke.  If a method is
//  found, then the arguments are mangled and passed in to the function call.
//
//===----------------------------------------------------------------------===//

#include "Interpreter.h"
#include "llvm/DerivedTypes.h"
#include <map>
#include <dlfcn.h>
#include <link.h>
#include <math.h>

typedef GenericValue (*ExFunc)(MethodType *, const vector<GenericValue> &);
static map<const Method *, ExFunc> Functions;
static map<string, ExFunc> FuncNames;

static Interpreter *TheInterpreter;

// getCurrentExecutablePath() - Return the directory that the lli executable
// lives in.
//
string Interpreter::getCurrentExecutablePath() const {
  Dl_info Info;
  if (dladdr(&TheInterpreter, &Info) == 0) return "";

  string LinkAddr(Info.dli_fname);
  unsigned SlashPos = LinkAddr.rfind('/');
  if (SlashPos != string::npos)
    LinkAddr.resize(SlashPos);    // Trim the executable name off...

  return LinkAddr;
}

static char getTypeID(const Type *Ty) {
  switch (Ty->getPrimitiveID()) {
  case Type::VoidTyID:    return 'V';
  case Type::BoolTyID:    return 'o';
  case Type::UByteTyID:   return 'B';
  case Type::SByteTyID:   return 'b';
  case Type::UShortTyID:  return 'S';
  case Type::ShortTyID:   return 's';
  case Type::UIntTyID:    return 'I';
  case Type::IntTyID:     return 'i';
  case Type::ULongTyID:   return 'L';
  case Type::LongTyID:    return 'l';
  case Type::FloatTyID:   return 'F';
  case Type::DoubleTyID:  return 'D';
  case Type::PointerTyID: return 'P';
  case Type::MethodTyID:  return 'M';
  case Type::StructTyID:  return 'T';
  case Type::ArrayTyID:   return 'A';
  case Type::OpaqueTyID:  return 'O';
  default: return 'U';
  }
}

static ExFunc lookupMethod(const Method *M) {
  // Function not found, look it up... start by figuring out what the
  // composite function name should be.
  string ExtName = "lle_";
  const MethodType *MT = M->getMethodType();
  for (unsigned i = 0; const Type *Ty = MT->getContainedType(i); ++i)
    ExtName += getTypeID(Ty);
  ExtName += "_" + M->getName();

  //cout << "Tried: '" << ExtName << "'\n";
  ExFunc FnPtr = FuncNames[ExtName];
  if (FnPtr == 0)
    FnPtr = (ExFunc)dlsym(RTLD_DEFAULT, ExtName.c_str());
  if (FnPtr == 0)
    FnPtr = FuncNames["lle_X_"+M->getName()];
  if (FnPtr == 0)  // Try calling a generic function... if it exists...
    FnPtr = (ExFunc)dlsym(RTLD_DEFAULT, ("lle_X_"+M->getName()).c_str());
  if (FnPtr != 0)
    Functions.insert(make_pair(M, FnPtr));  // Cache for later
  return FnPtr;
}

GenericValue Interpreter::callExternalMethod(Method *M,
                                         const vector<GenericValue> &ArgVals) {
  TheInterpreter = this;

  // Do a lookup to see if the method is in our cache... this should just be a
  // defered annotation!
  map<const Method *, ExFunc>::iterator FI = Functions.find(M);
  ExFunc Fn = (FI == Functions.end()) ? lookupMethod(M) : FI->second;
  if (Fn == 0) {
    cout << "Tried to execute an unknown external method: "
	 << M->getType()->getDescription() << " " << M->getName() << endl;
    return GenericValue();
  }

  // TODO: FIXME when types are not const!
  GenericValue Result = Fn(const_cast<MethodType*>(M->getMethodType()),ArgVals);
  return Result;
}

//===----------------------------------------------------------------------===//
//  Methods "exported" to the running application...
//
extern "C" {  // Don't add C++ manglings to llvm mangling :)

// Implement void printstr([ubyte {x N}] *)
GenericValue lle_VP_printstr(MethodType *M, const vector<GenericValue> &ArgVal){
  assert(ArgVal.size() == 1 && "printstr only takes one argument!");
  cout << (char*)ArgVal[0].PointerVal;
  return GenericValue();
}

// Implement 'void print(X)' for every type...
GenericValue lle_X_print(MethodType *M, const vector<GenericValue> &ArgVals) {
  assert(ArgVals.size() == 1 && "generic print only takes one argument!");

  Interpreter::print(M->getParamTypes()[0], ArgVals[0]);
  return GenericValue();
}

// Implement 'void printVal(X)' for every type...
GenericValue lle_X_printVal(MethodType *M, const vector<GenericValue> &ArgVal) {
  assert(ArgVal.size() == 1 && "generic print only takes one argument!");

  // Specialize print([ubyte {x N} ] *) and print(sbyte *)
  if (PointerType *PTy = dyn_cast<PointerType>(M->getParamTypes()[0].get()))
    if (PTy->getValueType() == Type::SByteTy ||
        isa<ArrayType>(PTy->getValueType())) {
      return lle_VP_printstr(M, ArgVal);
    }

  Interpreter::printValue(M->getParamTypes()[0], ArgVal[0]);
  return GenericValue();
}

// Implement 'void printString(X)'
// Argument must be [ubyte {x N} ] * or sbyte *
GenericValue lle_X_printString(MethodType *M, const vector<GenericValue> &ArgVal) {
  assert(ArgVal.size() == 1 && "generic print only takes one argument!");
  return lle_VP_printstr(M, ArgVal);
}

// Implement 'void print<TYPE>(X)' for each primitive type or pointer type
#define PRINT_TYPE_FUNC(TYPENAME,TYPEID) \
  GenericValue lle_X_print##TYPENAME(MethodType *M,\
                                     const vector<GenericValue> &ArgVal) {\
    assert(ArgVal.size() == 1 && "generic print only takes one argument!");\
    assert(M->getParamTypes()[0].get()->getPrimitiveID() == Type::##TYPEID);\
    Interpreter::printValue(M->getParamTypes()[0], ArgVal[0]);\
    return GenericValue();\
  }

PRINT_TYPE_FUNC(SByte,   SByteTyID)
PRINT_TYPE_FUNC(UByte,   UByteTyID)
PRINT_TYPE_FUNC(Short,   ShortTyID)
PRINT_TYPE_FUNC(UShort,  UShortTyID)
PRINT_TYPE_FUNC(Int,     IntTyID)
PRINT_TYPE_FUNC(UInt,    UIntTyID)
PRINT_TYPE_FUNC(Long,    LongTyID)
PRINT_TYPE_FUNC(ULong,   ULongTyID)
PRINT_TYPE_FUNC(Float,   FloatTyID)
PRINT_TYPE_FUNC(Double,  DoubleTyID)
PRINT_TYPE_FUNC(Pointer, PointerTyID)

// void "putchar"(sbyte)
GenericValue lle_Vb_putchar(MethodType *M, const vector<GenericValue> &Args) {
  cout << Args[0].SByteVal;
  return GenericValue();
}

// int "putchar"(int)
GenericValue lle_ii_putchar(MethodType *M, const vector<GenericValue> &Args) {
  cout << ((char)Args[0].IntVal) << flush;
  return Args[0];
}

// void "putchar"(ubyte)
GenericValue lle_VB_putchar(MethodType *M, const vector<GenericValue> &Args) {
  cout << Args[0].SByteVal << flush;
  return Args[0];
}

// void "__main"()
GenericValue lle_V___main(MethodType *M, const vector<GenericValue> &Args) {
  return GenericValue();
}

// void "exit"(int)
GenericValue lle_X_exit(MethodType *M, const vector<GenericValue> &Args) {
  TheInterpreter->exitCalled(Args[0]);
  return GenericValue();
}

// void *malloc(uint)
GenericValue lle_X_malloc(MethodType *M, const vector<GenericValue> &Args) {
  assert(Args.size() == 1 && "Malloc expects one argument!");
  GenericValue GV;
  GV.PointerVal = (uint64_t)malloc(Args[0].UIntVal);
  return GV;
}

// void free(void *)
GenericValue lle_X_free(MethodType *M, const vector<GenericValue> &Args) {
  free((void*)Args[0].PointerVal);
  return GenericValue();
}

// double pow(double, double)
GenericValue lle_X_pow(MethodType *M, const vector<GenericValue> &Args) {
  GenericValue GV;
  GV.DoubleVal = pow(Args[0].DoubleVal, Args[1].DoubleVal);
  return GV;
}

// int printf(sbyte *, ...) - a very rough implementation to make output useful.
GenericValue lle_X_printf(MethodType *M, const vector<GenericValue> &Args) {
  const char *FmtStr = (const char *)Args[0].PointerVal;
  unsigned ArgNo = 1;

  // printf should return # chars printed.  This is completely incorrect, but
  // close enough for now.
  GenericValue GV; GV.IntVal = strlen(FmtStr);
  while (1) {
    switch (*FmtStr) {
    case 0: return GV;             // Null terminator...
    default:                       // Normal nonspecial character
      cout << *FmtStr++;
      break;
    case '\\': {                   // Handle escape codes
      char Buffer[3];
      Buffer[0] = *FmtStr++;
      Buffer[1] = *FmtStr++;
      Buffer[2] = 0;
      cout << Buffer;
      break;
    }
    case '%': {                    // Handle format specifiers
      bool isLong = false;
      ++FmtStr;
      if (*FmtStr == 'l') {
        isLong = true;
        FmtStr++;
      }

      switch (*FmtStr) {
      case '%': cout << *FmtStr; break;                             // %%
      case 'd':                                                     // %d %i
      case 'i': cout << Args[ArgNo++].IntVal; break;
      case 'u': cout << Args[ArgNo++].UIntVal; break;               // %u
      case 'o': cout << oct << Args[ArgNo++].UIntVal << dec; break; // %o
      case 'x':
      case 'X': cout << hex << Args[ArgNo++].UIntVal << dec; break; // %x %X
      case 'e': case 'E': case 'g': case 'G':                       // %[eEgG]
        cout /*<< std::scientific*/ << Args[ArgNo++].DoubleVal
             /*<< std::fixed*/; break;
      case 'f': cout << Args[ArgNo++].DoubleVal; break;             // %f
      case 'c': cout << Args[ArgNo++].UByteVal; break;              // %c
      case 's': cout << (char*)Args[ArgNo++].PointerVal; break;     // %s
      default:  cout << "<unknown printf code '" << *FmtStr << "'!>";
                ArgNo++; break;
      }
      ++FmtStr;
      break;
    }
    }
  }
}

} // End extern "C"

void Interpreter::initializeExternalMethods() {
  FuncNames["lle_VP_printstr"] = lle_VP_printstr;
  FuncNames["lle_X_print"] = lle_X_print;
  FuncNames["lle_X_printVal"] = lle_X_printVal;
  FuncNames["lle_X_printString"] = lle_X_printString;
  FuncNames["lle_X_printUByte"] = lle_X_printUByte;
  FuncNames["lle_X_printSByte"] = lle_X_printSByte;
  FuncNames["lle_X_printUShort"] = lle_X_printUShort;
  FuncNames["lle_X_printShort"] = lle_X_printShort;
  FuncNames["lle_X_printInt"] = lle_X_printInt;
  FuncNames["lle_X_printUInt"] = lle_X_printUInt;
  FuncNames["lle_X_printLong"] = lle_X_printLong;
  FuncNames["lle_X_printULong"] = lle_X_printULong;
  FuncNames["lle_X_printFloat"] = lle_X_printFloat;
  FuncNames["lle_X_printDouble"] = lle_X_printDouble;
  FuncNames["lle_X_printPointer"] = lle_X_printPointer;
  FuncNames["lle_Vb_putchar"]     = lle_Vb_putchar;
  FuncNames["lle_ii_putchar"]     = lle_ii_putchar;
  FuncNames["lle_VB_putchar"]     = lle_VB_putchar;
  FuncNames["lle_V___main"]       = lle_V___main;
  FuncNames["lle_X_exit"]         = lle_X_exit;
  FuncNames["lle_X_malloc"]       = lle_X_malloc;
  FuncNames["lle_X_free"]         = lle_X_free;
  FuncNames["lle_X_pow"]          = lle_X_pow;
  FuncNames["lle_X_printf"]       = lle_X_printf;
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 by Robert Bosch GmbH. 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 "iceoryx_posh/iceoryx_posh_types.hpp"
#include "iceoryx_posh/internal/popo/ports/publisher_port_roudi.hpp"
#include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp"
#include "iceoryx_posh/internal/popo/ports/subscriber_port_multi_producer.hpp"
#include "iceoryx_posh/internal/popo/ports/subscriber_port_single_producer.hpp"
#include "iceoryx_posh/internal/popo/ports/subscriber_port_user.hpp"
#include "iceoryx_posh/mepoo/mepoo_config.hpp"
#include "iceoryx_utils/cxx/generic_raii.hpp"
#include "iceoryx_utils/error_handling/error_handling.hpp"
#include "iceoryx_utils/internal/concurrent/smart_lock.hpp"
#include "test.hpp"

#include <chrono>
#include <sstream>
#include <thread>

using namespace ::testing;
using namespace iox::popo;
using namespace iox::capro;
using namespace iox::cxx;
using namespace iox::mepoo;
using namespace iox::posix;
using ::testing::Return;

struct DummySample
{
    uint64_t m_dummy{42};
};

static const ServiceDescription TEST_SERVICE_DESCRIPTION("x", "y", "z");
static const iox::ProcessName_t TEST_SUBSCRIBER_APP_NAME("mySubscriberApp");
static const iox::ProcessName_t TEST_PUBLISHER_APP_NAME("myPublisherApp");

static constexpr uint32_t NUMBER_OF_PUBLISHERS = 27u;
static constexpr uint32_t ITERATIONS = 1000u;

static constexpr uint32_t NUM_CHUNKS_IN_POOL = NUMBER_OF_PUBLISHERS * ITERATIONS;
static constexpr uint32_t SMALL_CHUNK = 128u;
static constexpr uint32_t CHUNK_META_INFO_SIZE = 256u;
static constexpr size_t MEMORY_SIZE = NUM_CHUNKS_IN_POOL * (SMALL_CHUNK + CHUNK_META_INFO_SIZE);
alignas(64) static uint8_t g_memory[MEMORY_SIZE];

class PortUser_IntegrationTest : public Test
{
  public:
    PortUser_IntegrationTest()
    {
        m_mempoolConfig.addMemPool({SMALL_CHUNK, NUM_CHUNKS_IN_POOL});
        m_memoryManager.configureMemoryManager(m_mempoolConfig, &m_memoryAllocator, &m_memoryAllocator);
    }

    ~PortUser_IntegrationTest()
    {
    }

    void SetUp()
    {
        for (uint32_t i = 0; i < NUMBER_OF_PUBLISHERS; i++)
        {
            std::stringstream publisherAppName;
            publisherAppName << TEST_PUBLISHER_APP_NAME << i;

            iox::cxx::string<100> processName(TruncateToCapacity, publisherAppName.str().c_str());

            m_publisherPortDataVector.emplace_back(TEST_SERVICE_DESCRIPTION, processName, &m_memoryManager);
            m_publisherPortUserVector.emplace_back(&m_publisherPortDataVector.back());
            m_publisherPortRouDiVector.emplace_back(&m_publisherPortDataVector.back());
        }
    }

    void TearDown()
    {
        for (uint32_t i = 0; i < NUMBER_OF_PUBLISHERS; i++)
        {
            m_publisherPortUserVector[i].stopOffer();
            static_cast<void>(m_publisherPortRouDiVector[i].getCaProMessage());
        }

        m_subscriberPortUserSingleProducer.unsubscribe();
        m_subscriberPortUserMultiProducer.unsubscribe();

        static_cast<void>(m_subscriberPortRouDiSingleProducer.getCaProMessage());
        static_cast<void>(m_subscriberPortRouDiMultiProducer.getCaProMessage());
    }

    GenericRAII m_uniqueRouDiId{[] { iox::popo::internal::setUniqueRouDiId(0); },
                                [] { iox::popo::internal::unsetUniqueRouDiId(); }};

    uint64_t m_receiveCounter{0};
    std::atomic<uint64_t> m_sendCounter{0};
    std::atomic<uint64_t> m_publisherRunFinished{0};

    // Memory objects
    Allocator m_memoryAllocator{g_memory, MEMORY_SIZE};
    MePooConfig m_mempoolConfig;
    MemoryManager m_memoryManager;

    using ConcurrentCaproMessageVector_t = iox::concurrent::smart_lock<vector<CaproMessage, 1>>;
    ConcurrentCaproMessageVector_t m_concurrentCaproMessageVector;
    iox::concurrent::smart_lock<vector<CaproMessage, 1>> m_caproMessageRx;

    // subscriber port for single producer
    SubscriberPortData m_subscriberPortDataSingleProducer{
        TEST_SERVICE_DESCRIPTION, TEST_SUBSCRIBER_APP_NAME, VariantQueueTypes::SoFi_SingleProducerSingleConsumer};
    SubscriberPortUser m_subscriberPortUserSingleProducer{&m_subscriberPortDataSingleProducer};
    SubscriberPortSingleProducer m_subscriberPortRouDiSingleProducer{&m_subscriberPortDataSingleProducer};

    // subscriber port for multi producer
    SubscriberPortData m_subscriberPortDataMultiProducer{
        TEST_SERVICE_DESCRIPTION, TEST_SUBSCRIBER_APP_NAME, VariantQueueTypes::SoFi_MultiProducerSingleConsumer};
    SubscriberPortUser m_subscriberPortUserMultiProducer{&m_subscriberPortDataMultiProducer};
    SubscriberPortMultiProducer m_subscriberPortRouDiMultiProducer{&m_subscriberPortDataMultiProducer};

    // publisher port
    vector<PublisherPortData, NUMBER_OF_PUBLISHERS> m_publisherPortDataVector;
    vector<PublisherPortUser, NUMBER_OF_PUBLISHERS> m_publisherPortUserVector;
    vector<PublisherPortRouDi, NUMBER_OF_PUBLISHERS> m_publisherPortRouDiVector;

    inline CaproMessage waitForCaproMessage(const ConcurrentCaproMessageVector_t& concurrentCaproMessageVector,
                                            const CaproMessageType& caproMessageType)
    {
        bool finished{false};
        CaproMessage caproMessage;

        // Wait until the expected CaPro message has arrived in the shared message vector between subscriber and the
        // first publisher thread
        do
        {
            // Add delay to allow other thread accessing the shared resource
            std::this_thread::sleep_for(std::chrono::microseconds(10));
            {
                auto guardedVector = concurrentCaproMessageVector.GetScopeGuard();
                if (guardedVector->size() != 0)
                {
                    caproMessage = guardedVector->back();

                    if (caproMessage.m_type == caproMessageType)
                    {
                        guardedVector->pop_back();
                        finished = true;
                    }
                }
            }
        } while (!finished);

        return caproMessage;
    }

    template <typename SubscriberPortProducerType>
    void subscriberThread(uint32_t numberOfPublishers,
                          SubscriberPortProducerType& subscriberPortRouDi,
                          SubscriberPortUser& subscriberPortUser)
    {
        bool finished{false};

        // Wait for publisher to be ready
        auto caproMessage = waitForCaproMessage(m_concurrentCaproMessageVector, CaproMessageType::OFFER);

        // Subscribe to publisher
        subscriberPortUser.subscribe();
        auto maybeCaproMessage = subscriberPortRouDi.getCaProMessage();
        if (maybeCaproMessage.has_value())
        {
            caproMessage = maybeCaproMessage.value();
            m_concurrentCaproMessageVector->push_back(caproMessage);
        }
        else
        {
            // Error shall never occur
            FAIL() << "Error in subscriber SUB CaPro message!";
        }

        // Wait for subscription ACK from publisher
        caproMessage = waitForCaproMessage(m_concurrentCaproMessageVector, CaproMessageType::ACK);

        // Let RouDi change state to finish subscription
        static_cast<void>(subscriberPortRouDi.dispatchCaProMessage(caproMessage));

        // Subscription done and ready to receive samples
        while (!finished)
        {
            // Try to receive chunk
            subscriberPortUser.getChunk()
                .and_then([&](optional<const ChunkHeader*>& maybeChunkHeader) {
                    if (maybeChunkHeader.has_value())
                    {
                        auto chunkHeader = maybeChunkHeader.value();
                        m_receiveCounter++;
                        subscriberPortUser.releaseChunk(chunkHeader);
                    }
                    else
                    {
                        // Nothing received -> check if publisher(s) still running
                        if (m_publisherRunFinished.load(std::memory_order_relaxed) == numberOfPublishers)
                        {
                            finished = true;
                        }
                    }
                })
                .or_else([](ChunkReceiveError error) {
                    // Errors shall never occur
                    FAIL() << "Error in getChunk(): " << static_cast<uint32_t>(error);
                });
        }
    }

    void publisherThread(uint32_t publisherThreadIndex,
                         PublisherPortRouDi& publisherPortRouDi,
                         PublisherPortUser& publisherPortUser)
    {
        CaproMessage caproMessage;

        // Publisher offers its service
        publisherPortUser.offer();

        // Let RouDi change state and send OFFER to subscriber
        auto maybeCaproMessage = publisherPortRouDi.getCaProMessage();

        if (publisherThreadIndex == 0)
        {
            // First publisher thread will sync with subscriber
            if (maybeCaproMessage.has_value())
            {
                caproMessage = maybeCaproMessage.value();
                m_concurrentCaproMessageVector->push_back(caproMessage);
            }
            else
            {
                // Error shall never occur
                FAIL() << "Error in publisher OFFER CaPro message!";
            }

            // Wait for subscriber to subscribe
            caproMessage = waitForCaproMessage(m_concurrentCaproMessageVector, CaproMessageType::SUB);
            m_caproMessageRx->push_back(caproMessage);

            // Send ACK to subscriber
            maybeCaproMessage = publisherPortRouDi.dispatchCaProMessage(m_caproMessageRx->back());
            if (maybeCaproMessage.has_value())
            {
                caproMessage = maybeCaproMessage.value();
                m_concurrentCaproMessageVector->push_back(caproMessage);
            }
            else
            {
                // Error shall never occur
                FAIL() << "Error in publisher ACK CaPro message!";
            }
        }
        else
        {
            // All other publisher threads wait for the first thread to be synced with subscriber (indicated by
            // receiving a SUB message) to continue
            CaproMessage caproMessageRouDi(CaproMessageType::UNSUB, TEST_SERVICE_DESCRIPTION);
            do
            {
                std::this_thread::sleep_for(std::chrono::microseconds(10));

                if (m_caproMessageRx->size() != 0)
                {
                    caproMessageRouDi = m_caproMessageRx->back();
                }

            } while (caproMessageRouDi.m_type != CaproMessageType::SUB);

            static_cast<void>(publisherPortRouDi.dispatchCaProMessage(caproMessageRouDi));
        }

        // Subscriber is ready to receive -> start sending samples
        for (size_t i = 0; i < ITERATIONS; i++)
        {
            publisherPortUser.allocateChunk(sizeof(DummySample))
                .and_then([&](ChunkHeader* chunkHeader) {
                    auto sample = chunkHeader->payload();
                    new (sample) DummySample();
                    static_cast<DummySample*>(sample)->m_dummy = i;
                    publisherPortUser.sendChunk(chunkHeader);
                    m_sendCounter++;
                })
                .or_else([](AllocationError error) {
                    // Errors shall never occur
                    FAIL() << "Error in allocateChunk(): " << static_cast<uint32_t>(error);
                });

            /// Add some jitter to make thread breathe
            std::this_thread::sleep_for(std::chrono::microseconds(rand() % 750));
        }

        // Signal the subscriber thread we're done
        m_publisherRunFinished++;
    }
};

TEST_F(PortUser_IntegrationTest, SingleProducer)
{
    std::thread subscribingThread(std::bind(&PortUser_IntegrationTest::subscriberThread<SubscriberPortSingleProducer>,
                                            this,
                                            1,
                                            std::ref(PortUser_IntegrationTest::m_subscriberPortRouDiSingleProducer),
                                            std::ref(PortUser_IntegrationTest::m_subscriberPortUserSingleProducer)));
    std::thread publishingThread(std::bind(&PortUser_IntegrationTest::publisherThread,
                                           this,
                                           0,
                                           std::ref(PortUser_IntegrationTest::m_publisherPortRouDiVector.front()),
                                           std::ref(PortUser_IntegrationTest::m_publisherPortUserVector.front())));

    if (subscribingThread.joinable())
    {
        subscribingThread.join();
    }

    if (publishingThread.joinable())
    {
        publishingThread.join();
    }

    EXPECT_EQ(m_sendCounter.load(std::memory_order_relaxed), m_receiveCounter);
    EXPECT_EQ(PortUser_IntegrationTest::m_subscriberPortUserMultiProducer.hasLostChunks(), false);
}

TEST_F(PortUser_IntegrationTest, MultiProducer)
{
    std::thread subscribingThread(std::bind(&PortUser_IntegrationTest::subscriberThread<SubscriberPortMultiProducer>,
                                            this,
                                            NUMBER_OF_PUBLISHERS,
                                            std::ref(PortUser_IntegrationTest::m_subscriberPortRouDiMultiProducer),
                                            std::ref(PortUser_IntegrationTest::m_subscriberPortUserMultiProducer)));

    vector<std::thread, NUMBER_OF_PUBLISHERS> publisherThreadVector;
    for (uint32_t i = 0; i < NUMBER_OF_PUBLISHERS; i++)
    {
        publisherThreadVector.emplace_back(std::bind(&PortUser_IntegrationTest::publisherThread,
                                                     this,
                                                     i,
                                                     std::ref(PortUser_IntegrationTest::m_publisherPortRouDiVector[i]),
                                                     std::ref(PortUser_IntegrationTest::m_publisherPortUserVector[i])));
    }

    if (subscribingThread.joinable())
    {
        subscribingThread.join();
    }

    for (uint32_t i = 0; i < NUMBER_OF_PUBLISHERS; i++)
    {
        if (publisherThreadVector[i].joinable())
        {
            publisherThreadVector[i].join();
        }
    }

    EXPECT_EQ(m_sendCounter.load(std::memory_order_relaxed), m_receiveCounter);
    EXPECT_EQ(PortUser_IntegrationTest::m_subscriberPortUserMultiProducer.hasLostChunks(), false);
}
<commit_msg>iox-#25: Improve concurrent var names<commit_after>// Copyright (c) 2020 by Robert Bosch GmbH. 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 "iceoryx_posh/iceoryx_posh_types.hpp"
#include "iceoryx_posh/internal/popo/ports/publisher_port_roudi.hpp"
#include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp"
#include "iceoryx_posh/internal/popo/ports/subscriber_port_multi_producer.hpp"
#include "iceoryx_posh/internal/popo/ports/subscriber_port_single_producer.hpp"
#include "iceoryx_posh/internal/popo/ports/subscriber_port_user.hpp"
#include "iceoryx_posh/mepoo/mepoo_config.hpp"
#include "iceoryx_utils/cxx/generic_raii.hpp"
#include "iceoryx_utils/error_handling/error_handling.hpp"
#include "iceoryx_utils/internal/concurrent/smart_lock.hpp"
#include "test.hpp"

#include <chrono>
#include <sstream>
#include <thread>

using namespace ::testing;
using namespace iox::popo;
using namespace iox::capro;
using namespace iox::cxx;
using namespace iox::mepoo;
using namespace iox::posix;
using ::testing::Return;

struct DummySample
{
    uint64_t m_dummy{42};
};

static const ServiceDescription TEST_SERVICE_DESCRIPTION("x", "y", "z");
static const iox::ProcessName_t TEST_SUBSCRIBER_APP_NAME("mySubscriberApp");
static const iox::ProcessName_t TEST_PUBLISHER_APP_NAME("myPublisherApp");

static constexpr uint32_t NUMBER_OF_PUBLISHERS = 27u;
static constexpr uint32_t ITERATIONS = 1000u;

static constexpr uint32_t NUM_CHUNKS_IN_POOL = NUMBER_OF_PUBLISHERS * ITERATIONS;
static constexpr uint32_t SMALL_CHUNK = 128u;
static constexpr uint32_t CHUNK_META_INFO_SIZE = 256u;
static constexpr size_t MEMORY_SIZE = NUM_CHUNKS_IN_POOL * (SMALL_CHUNK + CHUNK_META_INFO_SIZE);
alignas(64) static uint8_t g_memory[MEMORY_SIZE];

class PortUser_IntegrationTest : public Test
{
  public:
    PortUser_IntegrationTest()
    {
        m_mempoolConfig.addMemPool({SMALL_CHUNK, NUM_CHUNKS_IN_POOL});
        m_memoryManager.configureMemoryManager(m_mempoolConfig, &m_memoryAllocator, &m_memoryAllocator);
    }

    ~PortUser_IntegrationTest()
    {
    }

    void SetUp()
    {
        for (uint32_t i = 0; i < NUMBER_OF_PUBLISHERS; i++)
        {
            std::stringstream publisherAppName;
            publisherAppName << TEST_PUBLISHER_APP_NAME << i;

            iox::cxx::string<100> processName(TruncateToCapacity, publisherAppName.str().c_str());

            m_publisherPortDataVector.emplace_back(TEST_SERVICE_DESCRIPTION, processName, &m_memoryManager);
            m_publisherPortUserVector.emplace_back(&m_publisherPortDataVector.back());
            m_publisherPortRouDiVector.emplace_back(&m_publisherPortDataVector.back());
        }
    }

    void TearDown()
    {
        for (uint32_t i = 0; i < NUMBER_OF_PUBLISHERS; i++)
        {
            m_publisherPortUserVector[i].stopOffer();
            static_cast<void>(m_publisherPortRouDiVector[i].getCaProMessage());
        }

        m_subscriberPortUserSingleProducer.unsubscribe();
        m_subscriberPortUserMultiProducer.unsubscribe();

        static_cast<void>(m_subscriberPortRouDiSingleProducer.getCaProMessage());
        static_cast<void>(m_subscriberPortRouDiMultiProducer.getCaProMessage());
    }

    GenericRAII m_uniqueRouDiId{[] { iox::popo::internal::setUniqueRouDiId(0); },
                                [] { iox::popo::internal::unsetUniqueRouDiId(); }};

    uint64_t m_receiveCounter{0};
    std::atomic<uint64_t> m_sendCounter{0};
    std::atomic<uint64_t> m_publisherRunFinished{0};

    // Memory objects
    Allocator m_memoryAllocator{g_memory, MEMORY_SIZE};
    MePooConfig m_mempoolConfig;
    MemoryManager m_memoryManager;

    using ConcurrentCaproMessageVector_t = iox::concurrent::smart_lock<vector<CaproMessage, 1>>;
    ConcurrentCaproMessageVector_t m_concurrentCaproMessageExchange;
    ConcurrentCaproMessageVector_t m_concurrentCaproMessageRx;

    // subscriber port for single producer
    SubscriberPortData m_subscriberPortDataSingleProducer{
        TEST_SERVICE_DESCRIPTION, TEST_SUBSCRIBER_APP_NAME, VariantQueueTypes::SoFi_SingleProducerSingleConsumer};
    SubscriberPortUser m_subscriberPortUserSingleProducer{&m_subscriberPortDataSingleProducer};
    SubscriberPortSingleProducer m_subscriberPortRouDiSingleProducer{&m_subscriberPortDataSingleProducer};

    // subscriber port for multi producer
    SubscriberPortData m_subscriberPortDataMultiProducer{
        TEST_SERVICE_DESCRIPTION, TEST_SUBSCRIBER_APP_NAME, VariantQueueTypes::SoFi_MultiProducerSingleConsumer};
    SubscriberPortUser m_subscriberPortUserMultiProducer{&m_subscriberPortDataMultiProducer};
    SubscriberPortMultiProducer m_subscriberPortRouDiMultiProducer{&m_subscriberPortDataMultiProducer};

    // publisher port
    vector<PublisherPortData, NUMBER_OF_PUBLISHERS> m_publisherPortDataVector;
    vector<PublisherPortUser, NUMBER_OF_PUBLISHERS> m_publisherPortUserVector;
    vector<PublisherPortRouDi, NUMBER_OF_PUBLISHERS> m_publisherPortRouDiVector;

    inline CaproMessage waitForCaproMessage(const ConcurrentCaproMessageVector_t& concurrentCaproMessageVector,
                                            const CaproMessageType& caproMessageType)
    {
        bool finished{false};
        CaproMessage caproMessage;

        // Wait until the expected CaPro message has arrived in the shared message vector between subscriber and the
        // first publisher thread
        do
        {
            // Add delay to allow other thread accessing the shared resource
            std::this_thread::sleep_for(std::chrono::microseconds(10));
            {
                auto guardedVector = concurrentCaproMessageVector.GetScopeGuard();
                if (guardedVector->size() != 0)
                {
                    caproMessage = guardedVector->back();

                    if (caproMessage.m_type == caproMessageType)
                    {
                        guardedVector->pop_back();
                        finished = true;
                    }
                }
            }
        } while (!finished);

        return caproMessage;
    }

    template <typename SubscriberPortProducerType>
    void subscriberThread(uint32_t numberOfPublishers,
                          SubscriberPortProducerType& subscriberPortRouDi,
                          SubscriberPortUser& subscriberPortUser)
    {
        bool finished{false};

        // Wait for publisher to be ready
        auto caproMessage = waitForCaproMessage(m_concurrentCaproMessageExchange, CaproMessageType::OFFER);

        // Subscribe to publisher
        subscriberPortUser.subscribe();
        auto maybeCaproMessage = subscriberPortRouDi.getCaProMessage();
        if (maybeCaproMessage.has_value())
        {
            caproMessage = maybeCaproMessage.value();
            m_concurrentCaproMessageExchange->push_back(caproMessage);
        }
        else
        {
            // Error shall never occur
            FAIL() << "Error in subscriber SUB CaPro message!";
        }

        // Wait for subscription ACK from publisher
        caproMessage = waitForCaproMessage(m_concurrentCaproMessageExchange, CaproMessageType::ACK);

        // Let RouDi change state to finish subscription
        static_cast<void>(subscriberPortRouDi.dispatchCaProMessage(caproMessage));

        // Subscription done and ready to receive samples
        while (!finished)
        {
            // Try to receive chunk
            subscriberPortUser.getChunk()
                .and_then([&](optional<const ChunkHeader*>& maybeChunkHeader) {
                    if (maybeChunkHeader.has_value())
                    {
                        auto chunkHeader = maybeChunkHeader.value();
                        m_receiveCounter++;
                        subscriberPortUser.releaseChunk(chunkHeader);
                    }
                    else
                    {
                        // Nothing received -> check if publisher(s) still running
                        if (m_publisherRunFinished.load(std::memory_order_relaxed) == numberOfPublishers)
                        {
                            finished = true;
                        }
                    }
                })
                .or_else([](ChunkReceiveError error) {
                    // Errors shall never occur
                    FAIL() << "Error in getChunk(): " << static_cast<uint32_t>(error);
                });
        }
    }

    void publisherThread(uint32_t publisherThreadIndex,
                         PublisherPortRouDi& publisherPortRouDi,
                         PublisherPortUser& publisherPortUser)
    {
        CaproMessage caproMessage;

        // Publisher offers its service
        publisherPortUser.offer();

        // Let RouDi change state and send OFFER to subscriber
        auto maybeCaproMessage = publisherPortRouDi.getCaProMessage();

        if (publisherThreadIndex == 0)
        {
            // First publisher thread will sync with subscriber
            if (maybeCaproMessage.has_value())
            {
                caproMessage = maybeCaproMessage.value();
                m_concurrentCaproMessageExchange->push_back(caproMessage);
            }
            else
            {
                // Error shall never occur
                FAIL() << "Error in publisher OFFER CaPro message!";
            }

            // Wait for subscriber to subscribe
            caproMessage = waitForCaproMessage(m_concurrentCaproMessageExchange, CaproMessageType::SUB);
            m_concurrentCaproMessageRx->push_back(caproMessage);

            // Send ACK to subscriber
            maybeCaproMessage = publisherPortRouDi.dispatchCaProMessage(m_concurrentCaproMessageRx->back());
            if (maybeCaproMessage.has_value())
            {
                caproMessage = maybeCaproMessage.value();
                m_concurrentCaproMessageExchange->push_back(caproMessage);
            }
            else
            {
                // Error shall never occur
                FAIL() << "Error in publisher ACK CaPro message!";
            }
        }
        else
        {
            // All other publisher threads wait for the first thread to be synced with subscriber (indicated by
            // receiving a SUB message) to continue
            CaproMessage caproMessageRouDi(CaproMessageType::UNSUB, TEST_SERVICE_DESCRIPTION);
            do
            {
                std::this_thread::sleep_for(std::chrono::microseconds(10));

                if (m_concurrentCaproMessageRx->size() != 0)
                {
                    caproMessageRouDi = m_concurrentCaproMessageRx->back();
                }

            } while (caproMessageRouDi.m_type != CaproMessageType::SUB);

            static_cast<void>(publisherPortRouDi.dispatchCaProMessage(caproMessageRouDi));
        }

        // Subscriber is ready to receive -> start sending samples
        for (size_t i = 0; i < ITERATIONS; i++)
        {
            publisherPortUser.allocateChunk(sizeof(DummySample))
                .and_then([&](ChunkHeader* chunkHeader) {
                    auto sample = chunkHeader->payload();
                    new (sample) DummySample();
                    static_cast<DummySample*>(sample)->m_dummy = i;
                    publisherPortUser.sendChunk(chunkHeader);
                    m_sendCounter++;
                })
                .or_else([](AllocationError error) {
                    // Errors shall never occur
                    FAIL() << "Error in allocateChunk(): " << static_cast<uint32_t>(error);
                });

            /// Add some jitter to make thread breathe
            std::this_thread::sleep_for(std::chrono::microseconds(rand() % 750));
        }

        // Signal the subscriber thread we're done
        m_publisherRunFinished++;
    }
};

TEST_F(PortUser_IntegrationTest, SingleProducer)
{
    std::thread subscribingThread(std::bind(&PortUser_IntegrationTest::subscriberThread<SubscriberPortSingleProducer>,
                                            this,
                                            1,
                                            std::ref(PortUser_IntegrationTest::m_subscriberPortRouDiSingleProducer),
                                            std::ref(PortUser_IntegrationTest::m_subscriberPortUserSingleProducer)));
    std::thread publishingThread(std::bind(&PortUser_IntegrationTest::publisherThread,
                                           this,
                                           0,
                                           std::ref(PortUser_IntegrationTest::m_publisherPortRouDiVector.front()),
                                           std::ref(PortUser_IntegrationTest::m_publisherPortUserVector.front())));

    if (subscribingThread.joinable())
    {
        subscribingThread.join();
    }

    if (publishingThread.joinable())
    {
        publishingThread.join();
    }

    EXPECT_EQ(m_sendCounter.load(std::memory_order_relaxed), m_receiveCounter);
    EXPECT_EQ(PortUser_IntegrationTest::m_subscriberPortUserMultiProducer.hasLostChunks(), false);
}

TEST_F(PortUser_IntegrationTest, MultiProducer)
{
    std::thread subscribingThread(std::bind(&PortUser_IntegrationTest::subscriberThread<SubscriberPortMultiProducer>,
                                            this,
                                            NUMBER_OF_PUBLISHERS,
                                            std::ref(PortUser_IntegrationTest::m_subscriberPortRouDiMultiProducer),
                                            std::ref(PortUser_IntegrationTest::m_subscriberPortUserMultiProducer)));

    vector<std::thread, NUMBER_OF_PUBLISHERS> publisherThreadVector;
    for (uint32_t i = 0; i < NUMBER_OF_PUBLISHERS; i++)
    {
        publisherThreadVector.emplace_back(std::bind(&PortUser_IntegrationTest::publisherThread,
                                                     this,
                                                     i,
                                                     std::ref(PortUser_IntegrationTest::m_publisherPortRouDiVector[i]),
                                                     std::ref(PortUser_IntegrationTest::m_publisherPortUserVector[i])));
    }

    if (subscribingThread.joinable())
    {
        subscribingThread.join();
    }

    for (uint32_t i = 0; i < NUMBER_OF_PUBLISHERS; i++)
    {
        if (publisherThreadVector[i].joinable())
        {
            publisherThreadVector[i].join();
        }
    }

    EXPECT_EQ(m_sendCounter.load(std::memory_order_relaxed), m_receiveCounter);
    EXPECT_EQ(PortUser_IntegrationTest::m_subscriberPortUserMultiProducer.hasLostChunks(), false);
}
<|endoftext|>


<commit_before>//
// Copyright (C) 2007-2017 SIPez LLC. All rights reserved
//
// $$
///////////////////////////////////////////////////////////////////////////////

// SYSTEM INCLUDES
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include <assert.h>

// APPLICATION INCLUDES
#include "mp/MpodAlsa.h"
#include "mp/MpOutputDeviceManager.h"
#include "os/OsTask.h"
#include "os/OsNotification.h"
#include "os/OsCallback.h"

#ifdef RTL_ENABLED // [
#  include "rtl_macro.h"
#else  // RTL_ENABLED ][
#  define RTL_BLOCK(x)
#  define RTL_EVENT(x, y)
#endif // RTL_ENABLED ]

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STATIC VARIABLE INITIALIZATIONS
char MpodAlsa::spDefaultDeviceName[MAX_DEVICE_NAME_SIZE];

/* //////////////////////////// PUBLIC //////////////////////////////////// */
/* ============================ CREATORS ================================== */
// Default constructor
MpodAlsa::MpodAlsa(const UtlString& name)
: MpOutputDeviceDriver(name)
, mAudioFrame(NULL)
, mNotificationThreadEn(FALSE)
{
   mpCont = MpAlsaContainer::getContainer();
   if (mpCont != NULL)
   {
      pDevWrapper = mpCont->getALSADeviceWrapper(name);
   }
   else
   {
      pDevWrapper = NULL;
   }
}

MpodAlsa::~MpodAlsa()
{
   // ALSA Device must be freed
   assert (!isDeviceValid());

   if (mpCont != NULL)
   {
      MpAlsaContainer::releaseContainer(mpCont);
   }
}
/* ============================ MANIPULATORS ============================== */
OsStatus MpodAlsa::setNotificationMode(UtlBoolean bThreadNotification)
{
   if (isEnabled())
   {
      return OS_INVALID_STATE;
   }

   mNotificationThreadEn = bThreadNotification;
   return OS_SUCCESS;
}

OsStatus MpodAlsa::enableDevice(unsigned samplesPerFrame,
                               unsigned samplesPerSec,
                               MpFrameTime currentFrameTime,
                               OsCallback &frameTicker)
{
   OsStatus ret;
   if (isEnabled())
   {
       return OS_FAILED;
   }

   //Opening ALSA device
   if (pDevWrapper)
   {
       mSamplesPerFrame = samplesPerFrame;
       mSamplesPerSec = samplesPerSec;
       OsStatus res = pDevWrapper->setOutputDevice(this);
       if (res != OS_SUCCESS)
       {
           pDevWrapper = NULL;
       }
       else if (!pDevWrapper->mbWriteCap)
       {
           //Device dosen't support output
           pDevWrapper->freeOutputDevice();
           pDevWrapper = NULL;
       }
   }

   // If the device is not valid, let the user know it's bad.
   if (!isDeviceValid())
   {
      return OS_INVALID_STATE;
   }

   // Set some wave header stat information.
   mCurrentFrameTime = currentFrameTime;
   mpTickerNotification = &frameTicker;

   // Get buffer for audio data and fill it with silence
   mAudioFrame = new MpAudioSample[samplesPerFrame];
   if (mAudioFrame == NULL)
   {
      return OS_LIMIT_REACHED;
   }
   memset(mAudioFrame, 0, samplesPerFrame * sizeof(MpAudioSample));

   ret = pDevWrapper->attachWriter();
   if (ret != OS_SUCCESS)
   {
      return ret;
   }
   mIsEnabled = TRUE;

   return ret;
}

OsStatus MpodAlsa::disableDevice()
{
   OsStatus ret;
   if (!isEnabled())
   {
       return OS_FAILED;
   }

   // If the device is not valid, let the user know it's bad.
   if (!isDeviceValid())
   {
      return OS_INVALID_STATE;
   }

   ret = pDevWrapper->detachWriter();
   if (ret != OS_SUCCESS)
   {
      return ret;
   }
   delete[] mAudioFrame;
   mIsEnabled = FALSE;

   pDevWrapper->freeOutputDevice();

   return ret;
}

OsStatus MpodAlsa::pushFrame(unsigned int numSamples,
                            const MpAudioSample* samples,
                            MpFrameTime frameTime)
{
   if (!isEnabled())
      return OS_FAILED;

   // Currently only full frame supported
   if(numSamples != mSamplesPerFrame)
   {
      OsSysLog::add(FAC_MP, PRI_ERR,
         "MpodAlsa::pushFrame given %d samples, expected full frame of: %d samples",
         numSamples, (int)mSamplesPerFrame);
      OsSysLog::flush();
      assert(numSamples == mSamplesPerFrame);
   }

   RTL_BLOCK("MpodAlsa::pushFrame");
   if (samples != NULL)
   {
      memcpy(mAudioFrame, samples, numSamples * sizeof(MpAudioSample));
   }

   return OS_SUCCESS;
}

/* ============================ ACCESSORS ================================= */

const char* MpodAlsa::getDefaultDeviceName()
{
    UtlSList deviceNames;

    // Get the list of available output devices
    getDeviceNames(deviceNames);

    // The first one is the default
    UtlString* firstDevice = (UtlString*) deviceNames.get();
    strncpy(spDefaultDeviceName,
            firstDevice ? firstDevice->data() : "",
            MAX_DEVICE_NAME_SIZE);
    deviceNames.destroyAll();

    return(spDefaultDeviceName);
}

int MpodAlsa::getDeviceNames(UtlContainer& deviceNames)
{
    int deviceCount =
        MpAlsa::getDeviceNames(deviceNames,
                               false);  // output device names

    return(deviceCount);
}

/* ============================ INQUIRY =================================== */

OsStatus MpodAlsa::canEnable()
{
    OsStatus status = OS_FAILED;
    snd_pcm_t* pcmOut = NULL;

    int openStatus =
        snd_pcm_open(&pcmOut,
                     getDeviceName(),
		     SND_PCM_STREAM_PLAYBACK,
		     SND_PCM_NONBLOCK);
    switch(openStatus)
    {
    case 0:
        status = OS_SUCCESS;
        snd_pcm_close(pcmOut);
        break;

    case -2:
        status = OS_NOT_FOUND;
        break;

    case 16:
        status = OS_BUSY;
        break;

    default:
        status = OS_FAILED;
        break;
    }
    if(openStatus != 0)
    {
        OsSysLog::add(FAC_MP, PRI_ERR,
                      "MposAlsa::canEnable snd_pcm_open(%p, \"%s\", %s) returned: %d %s",
                      pcmOut,
                      getDeviceName().data(),
                      "SND_PCM_STREAM_PLAYBACK",
                      openStatus,
                      snd_strerror(openStatus));
    }

    return(status);
}

/* //////////////////////////// PROTECTED ///////////////////////////////// */

OsStatus MpodAlsa::signalForNextFrame()
{
   OsStatus ret = OS_FAILED;
   ret = mpTickerNotification->signal(mCurrentFrameTime);
#ifdef TEST_PRINT
   OsSysLog::add(FAC_MP, PRI_DEBUG,
                 "MpodAlsa::signalForNextFrame %s",
                 data());
#endif
   return ret;
}

void MpodAlsa::skipFrame()
{
   mCurrentFrameTime += getFramePeriod();
}

/* //////////////////////////// PRIVATE /////////////////////////////////// */

<commit_msg>Made MpodAlsa::disableDevice more tolerant of situations. It was very rigid of everything being in the "perfect" enabled state. Such that it would bail out of disabling sometimes, leaving the MpodAlsa with mIsEnabled == TRUE and no way to set it to FALSE.  Hense we get an assert when deleting the MpodAlsa as it is still "enabled".<commit_after>//
// Copyright (C) 2007-2017 SIPez LLC. All rights reserved
//
// $$
///////////////////////////////////////////////////////////////////////////////

// SYSTEM INCLUDES
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include <assert.h>

// APPLICATION INCLUDES
#include "mp/MpodAlsa.h"
#include "mp/MpOutputDeviceManager.h"
#include "os/OsTask.h"
#include "os/OsNotification.h"
#include "os/OsCallback.h"

#ifdef RTL_ENABLED // [
#  include "rtl_macro.h"
#else  // RTL_ENABLED ][
#  define RTL_BLOCK(x)
#  define RTL_EVENT(x, y)
#endif // RTL_ENABLED ]

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STATIC VARIABLE INITIALIZATIONS
char MpodAlsa::spDefaultDeviceName[MAX_DEVICE_NAME_SIZE];

/* //////////////////////////// PUBLIC //////////////////////////////////// */
/* ============================ CREATORS ================================== */
// Default constructor
MpodAlsa::MpodAlsa(const UtlString& name)
: MpOutputDeviceDriver(name)
, mAudioFrame(NULL)
, mNotificationThreadEn(FALSE)
{
   mpCont = MpAlsaContainer::getContainer();
   if (mpCont != NULL)
   {
      pDevWrapper = mpCont->getALSADeviceWrapper(name);
   }
   else
   {
      pDevWrapper = NULL;
   }
}

MpodAlsa::~MpodAlsa()
{
   // ALSA Device must be freed
   assert (!isDeviceValid());

   if (mpCont != NULL)
   {
      MpAlsaContainer::releaseContainer(mpCont);
   }
}
/* ============================ MANIPULATORS ============================== */
OsStatus MpodAlsa::setNotificationMode(UtlBoolean bThreadNotification)
{
   if (isEnabled())
   {
      return OS_INVALID_STATE;
   }

   mNotificationThreadEn = bThreadNotification;
   return OS_SUCCESS;
}

OsStatus MpodAlsa::enableDevice(unsigned samplesPerFrame,
                               unsigned samplesPerSec,
                               MpFrameTime currentFrameTime,
                               OsCallback &frameTicker)
{
#ifdef TEST_PRINT
   OsSysLog::add(FAC_MP, PRI_DEBUG,
                 "MpodAlsa::enableDevice start");
#endif
   OsStatus ret;
   if (isEnabled())
   {
       return OS_FAILED;
   }

   //Opening ALSA device
   if (pDevWrapper)
   {
       mSamplesPerFrame = samplesPerFrame;
       mSamplesPerSec = samplesPerSec;
       OsStatus res = pDevWrapper->setOutputDevice(this);
       if (res != OS_SUCCESS)
       {
           pDevWrapper = NULL;
       }
       else if (!pDevWrapper->mbWriteCap)
       {
           //Device dosen't support output
           pDevWrapper->freeOutputDevice();
           pDevWrapper = NULL;
       }
   }

   // If the device is not valid, let the user know it's bad.
   if (!isDeviceValid())
   {
      return OS_INVALID_STATE;
   }

   // Set some wave header stat information.
   mCurrentFrameTime = currentFrameTime;
   mpTickerNotification = &frameTicker;

   // Get buffer for audio data and fill it with silence
   mAudioFrame = new MpAudioSample[samplesPerFrame];
   if (mAudioFrame == NULL)
   {
      return OS_LIMIT_REACHED;
   }
   memset(mAudioFrame, 0, samplesPerFrame * sizeof(MpAudioSample));

   ret = pDevWrapper->attachWriter();
   if (ret != OS_SUCCESS)
   {
      return ret;
   }
#ifdef TEST_PRINT
   OsSysLog::add(FAC_MP, PRI_DEBUG,
                 "MpodAlsa::enableDevice set");
#endif
   mIsEnabled = TRUE;

   return ret;
}

OsStatus MpodAlsa::disableDevice()
{
#ifdef TEST_PRINT
   OsSysLog::add(FAC_MP, PRI_DEBUG,
                 "MpodAlsa::disableDevice start");
#endif
   OsStatus ret = OS_SUCCESS;
   if (!isEnabled())
   {
#ifdef TEST_PRINT
       OsSysLog::add(FAC_MP, PRI_DEBUG,
                     "MpodAlsa::disableDevice not enabled");
#endif
       return OS_FAILED;
   }

   // If the device is not valid, let the user know it's bad.
   if (!isDeviceValid())
   {
#ifdef TEST_PRINT
       OsSysLog::add(FAC_MP, PRI_DEBUG,
                     "MpodAlsa::disableDevice not valid");
#endif
      ret = OS_INVALID_STATE;
   }

   if(ret != OS_SUCCESS)
   {
       // Attempt the detach despite the prior error, but
       // don't hide the prior error.
       pDevWrapper->detachWriter();
#ifdef TEST_PRINT
       OsSysLog::add(FAC_MP, PRI_DEBUG,
                     "MpodAlsa::disableDevice detach no ret check");
#endif
   }
   else
   {
       ret = pDevWrapper->detachWriter();
       if (ret != OS_SUCCESS)
       {
#ifdef TEST_PRINT
           OsSysLog::add(FAC_MP, PRI_DEBUG,
                         "MpodAlsa::disableDevice detach ret: %d",
                         ret);
#endif
       }
   }
   delete[] mAudioFrame;
#ifdef TEST_PRINT
   OsSysLog::add(FAC_MP, PRI_DEBUG,
                 "MpodAlsa::disableDevice set");
#endif
   mIsEnabled = FALSE;

   pDevWrapper->freeOutputDevice();

   return ret;
}

OsStatus MpodAlsa::pushFrame(unsigned int numSamples,
                            const MpAudioSample* samples,
                            MpFrameTime frameTime)
{
   if (!isEnabled())
      return OS_FAILED;

   // Currently only full frame supported
   if(numSamples != mSamplesPerFrame)
   {
      OsSysLog::add(FAC_MP, PRI_ERR,
         "MpodAlsa::pushFrame given %d samples, expected full frame of: %d samples",
         numSamples, (int)mSamplesPerFrame);
      OsSysLog::flush();
      assert(numSamples == mSamplesPerFrame);
   }

   RTL_BLOCK("MpodAlsa::pushFrame");
   if (samples != NULL)
   {
      memcpy(mAudioFrame, samples, numSamples * sizeof(MpAudioSample));
   }

   return OS_SUCCESS;
}

/* ============================ ACCESSORS ================================= */

const char* MpodAlsa::getDefaultDeviceName()
{
    UtlSList deviceNames;

    // Get the list of available output devices
    getDeviceNames(deviceNames);

    // The first one is the default
    UtlString* firstDevice = (UtlString*) deviceNames.get();
    strncpy(spDefaultDeviceName,
            firstDevice ? firstDevice->data() : "",
            MAX_DEVICE_NAME_SIZE);
    deviceNames.destroyAll();

    return(spDefaultDeviceName);
}

int MpodAlsa::getDeviceNames(UtlContainer& deviceNames)
{
    int deviceCount =
        MpAlsa::getDeviceNames(deviceNames,
                               false);  // output device names

    return(deviceCount);
}

/* ============================ INQUIRY =================================== */

OsStatus MpodAlsa::canEnable()
{
    OsStatus status = OS_FAILED;
    snd_pcm_t* pcmOut = NULL;

    int openStatus =
        snd_pcm_open(&pcmOut,
                     getDeviceName(),
		     SND_PCM_STREAM_PLAYBACK,
		     SND_PCM_NONBLOCK);
    switch(openStatus)
    {
    case 0:
        status = OS_SUCCESS;
        snd_pcm_close(pcmOut);
        break;

    case -2:
        status = OS_NOT_FOUND;
        break;

    case 16:
        status = OS_BUSY;
        break;

    default:
        status = OS_FAILED;
        break;
    }
    if(openStatus != 0)
    {
        OsSysLog::add(FAC_MP, PRI_ERR,
                      "MposAlsa::canEnable snd_pcm_open(%p, \"%s\", %s) returned: %d %s",
                      pcmOut,
                      getDeviceName().data(),
                      "SND_PCM_STREAM_PLAYBACK",
                      openStatus,
                      snd_strerror(openStatus));
    }

    return(status);
}

/* //////////////////////////// PROTECTED ///////////////////////////////// */

OsStatus MpodAlsa::signalForNextFrame()
{
   OsStatus ret = OS_FAILED;
   ret = mpTickerNotification->signal(mCurrentFrameTime);
#ifdef TEST_PRINT
   OsSysLog::add(FAC_MP, PRI_DEBUG,
                 "MpodAlsa::signalForNextFrame %s",
                 data());
#endif
   return ret;
}

void MpodAlsa::skipFrame()
{
   mCurrentFrameTime += getFramePeriod();
}

/* //////////////////////////// PRIVATE /////////////////////////////////// */

<|endoftext|>