<commit_before>//=============================================================================
//
// Adventure Game Studio (AGS)
//
// Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
// The full list of copyright holders can be found in the Copyright.txt
// file, which is part of this source code distribution.
//
// The AGS source code is provided under the Artistic License 2.0.
// A copy of this license can be found in the file License.txt and at
// http://www.opensource.org/licenses/artistic-license-2.0.php
//
//=============================================================================

#ifdef _DEBUG

#include <stdio.h>
#include <string.h>
#include "util/alignedstream.h"
#include "util/filestream.h"
#include "debug/assert.h"

using AGS::Common::String;
using AGS::Common::DataStream;
using AGS::Common::AlignedStream;
namespace File = AGS::Common::File;

struct TTrickyAlignedData
{
    char    a;
    int     b;
    int     c;
    short   d[3];
    int     e;
    char    f[17];
    int     g[4];
    short   h[13];
};

void Test_File()
{
    //-----------------------------------------------------
    // Operations
    DataStream *out = File::OpenFile("test.tmp", AGS::Common::kFile_CreateAlways, AGS::Common::kFile_Write);

    out->WriteInt16(10);
    out->WriteInt64(-20202);
    String::WriteString("test.tmp", out);
    String very_long_string;
    very_long_string.FillString('a', 10000);
    very_long_string.Write(out);

    TTrickyAlignedData tricky_data_out;
    memset(&tricky_data_out, 0xAA, sizeof(tricky_data_out));
    {
        tricky_data_out.a = 11;
        tricky_data_out.b = 12;
        tricky_data_out.c = 13;
        tricky_data_out.d[0] = 14;
        tricky_data_out.d[1] = 15;
        tricky_data_out.d[2] = 16;
        tricky_data_out.e = 17;
        memset(tricky_data_out.f, 0, 17);
        tricky_data_out.g[0] = 18;
        tricky_data_out.g[1] = 19;
        tricky_data_out.g[2] = 20;
        tricky_data_out.g[3] = 21;
        memset(tricky_data_out.h, 0, 13 * sizeof(short));
#if defined (TEST_BIGENDIAN)
        TTrickyAlignedData bigend_data = tricky_data_out;
        AGS::Common::BitByteOperations::SwapBytesInt32(bigend_data.b);
        AGS::Common::BitByteOperations::SwapBytesInt32(bigend_data.c);
        for (int i = 0; i < 3; ++i)
        {
            AGS::Common::BitByteOperations::SwapBytesInt16(bigend_data.d[i]);
        }
        AGS::Common::BitByteOperations::SwapBytesInt32(bigend_data.e);
        for (int i = 0; i < 4; ++i)
        {
            AGS::Common::BitByteOperations::SwapBytesInt32(bigend_data.g[i]);
        }
        out->Write(&bigend_data, sizeof(TTrickyAlignedData));
#else
        out->Write(&tricky_data_out, sizeof(TTrickyAlignedData));
#endif
    }

    out->WriteInt32(20);

    intptr_t ptr32_array_out[4];
    ptr32_array_out[0] = 0xABCDABCD;
    ptr32_array_out[1] = 0xFEDCFEDC;
    ptr32_array_out[2] = 0xFEEDBEEF;
    ptr32_array_out[3] = 0xBEEFFEED;
    out->WriteArrayOfIntPtr32(ptr32_array_out, 4);

    delete out;

    //-------------------------------------------------------------------------

    DataStream *in = File::OpenFile("test.tmp", AGS::Common::kFile_Open, AGS::Common::kFile_Read);

    int16_t int16val    = in->ReadInt16();
    int64_t int64val    = in->ReadInt64();
    String str1         = String::FromStream(out);
    String str2         = String::FromStream(out);

    TTrickyAlignedData tricky_data_in;
    memset(&tricky_data_in, 0xAA, sizeof(tricky_data_out));
    {
        AlignedStream as(in, AGS::Common::kAligned_Read);
        tricky_data_in.a = as.ReadInt8();
        tricky_data_in.b = as.ReadInt32();
        tricky_data_in.c = as.ReadInt32();
        as.ReadArrayOfInt16(tricky_data_in.d, 3);
        tricky_data_in.e = as.ReadInt32();
        as.Read(tricky_data_in.f, 17);
        as.ReadArrayOfInt32(tricky_data_in.g, 4);
        as.ReadArrayOfInt16(tricky_data_in.h, 13);
        as.ReleaseStream(); // releasing filestream so that it won't be deleted
    }

    int32_t int32val    = in->ReadInt32();

    intptr_t ptr32_array_in[4];
    in->ReadArrayOfIntPtr32(ptr32_array_in, 4);

    delete in;

    File::DeleteFile("test.tmp");

    //-----------------------------------------------------
    // Assertions
    assert(int16val == 10);
    assert(int64val == -20202);
    assert(strcmp(str1, "test.tmp") == 0);
    assert(strcmp(str2, very_long_string) == 0);
    assert(memcmp(&tricky_data_in, &tricky_data_out, sizeof(TTrickyAlignedData)) == 0);
    assert(int32val == 20);

    assert(ptr32_array_in[0] == 0xABCDABCD);
    assert(ptr32_array_in[1] == 0xFEDCFEDC);
    assert(ptr32_array_in[2] == 0xFEEDBEEF);
    assert(ptr32_array_in[3] == 0xBEEFFEED);

    assert(!File::TestReadFile("test.tmp"));
}

#endif // _DEBUG
<commit_msg>Engine: fixed file test<commit_after>//=============================================================================
//
// Adventure Game Studio (AGS)
//
// Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
// The full list of copyright holders can be found in the Copyright.txt
// file, which is part of this source code distribution.
//
// The AGS source code is provided under the Artistic License 2.0.
// A copy of this license can be found in the file License.txt and at
// http://www.opensource.org/licenses/artistic-license-2.0.php
//
//=============================================================================

#ifdef _DEBUG

#include <stdio.h>
#include <string.h>
#include "util/alignedstream.h"
#include "util/filestream.h"
#include "debug/assert.h"

using AGS::Common::String;
using AGS::Common::DataStream;
using AGS::Common::AlignedStream;
namespace File = AGS::Common::File;

struct TTrickyAlignedData
{
    char    a;
    int     b;
    int     c;
    short   d[3];
    int     e;
    char    f[17];
    int     g[4];
    short   h[13];
};

void Test_File()
{
    //-----------------------------------------------------
    // Operations
    DataStream *out = File::OpenFile("test.tmp", AGS::Common::kFile_CreateAlways, AGS::Common::kFile_Write);

    out->WriteInt16(10);
    out->WriteInt64(-20202);
    String::WriteString("test.tmp", out);
    String very_long_string;
    very_long_string.FillString('a', 10000);
    very_long_string.Write(out);

    TTrickyAlignedData tricky_data_out;
    memset(&tricky_data_out, 0xAA, sizeof(tricky_data_out));
    {
        tricky_data_out.a = 11;
        tricky_data_out.b = 12;
        tricky_data_out.c = 13;
        tricky_data_out.d[0] = 14;
        tricky_data_out.d[1] = 15;
        tricky_data_out.d[2] = 16;
        tricky_data_out.e = 17;
        memset(tricky_data_out.f, 0, 17);
        tricky_data_out.g[0] = 18;
        tricky_data_out.g[1] = 19;
        tricky_data_out.g[2] = 20;
        tricky_data_out.g[3] = 21;
        memset(tricky_data_out.h, 0, 13 * sizeof(short));
#if defined (TEST_BIGENDIAN)
        TTrickyAlignedData bigend_data = tricky_data_out;
        AGS::Common::BitByteOperations::SwapBytesInt32(bigend_data.b);
        AGS::Common::BitByteOperations::SwapBytesInt32(bigend_data.c);
        for (int i = 0; i < 3; ++i)
        {
            AGS::Common::BitByteOperations::SwapBytesInt16(bigend_data.d[i]);
        }
        AGS::Common::BitByteOperations::SwapBytesInt32(bigend_data.e);
        for (int i = 0; i < 4; ++i)
        {
            AGS::Common::BitByteOperations::SwapBytesInt32(bigend_data.g[i]);
        }
        out->Write(&bigend_data, sizeof(TTrickyAlignedData));
#else
        out->Write(&tricky_data_out, sizeof(TTrickyAlignedData));
#endif
    }

    out->WriteInt32(20);

    intptr_t ptr32_array_out[4];
    ptr32_array_out[0] = 0xABCDABCD;
    ptr32_array_out[1] = 0xFEDCFEDC;
    ptr32_array_out[2] = 0xFEEDBEEF;
    ptr32_array_out[3] = 0xBEEFFEED;
    out->WriteArrayOfIntPtr32(ptr32_array_out, 4);

    delete out;

    //-------------------------------------------------------------------------

    DataStream *in = File::OpenFile("test.tmp", AGS::Common::kFile_Open, AGS::Common::kFile_Read);

    int16_t int16val    = in->ReadInt16();
    int64_t int64val    = in->ReadInt64();
    String str1         = String::FromStream(in);
    String str2         = String::FromStream(in);

    TTrickyAlignedData tricky_data_in;
    memset(&tricky_data_in, 0xAA, sizeof(tricky_data_out));
    {
        AlignedStream as(in, AGS::Common::kAligned_Read);
        tricky_data_in.a = as.ReadInt8();
        tricky_data_in.b = as.ReadInt32();
        tricky_data_in.c = as.ReadInt32();
        as.ReadArrayOfInt16(tricky_data_in.d, 3);
        tricky_data_in.e = as.ReadInt32();
        as.Read(tricky_data_in.f, 17);
        as.ReadArrayOfInt32(tricky_data_in.g, 4);
        as.ReadArrayOfInt16(tricky_data_in.h, 13);
        as.ReleaseStream(); // releasing filestream so that it won't be deleted
    }

    int32_t int32val    = in->ReadInt32();

    intptr_t ptr32_array_in[4];
    in->ReadArrayOfIntPtr32(ptr32_array_in, 4);

    delete in;

    File::DeleteFile("test.tmp");

    //-----------------------------------------------------
    // Assertions
    assert(int16val == 10);
    assert(int64val == -20202);
    assert(strcmp(str1, "test.tmp") == 0);
    assert(strcmp(str2, very_long_string) == 0);
    assert(memcmp(&tricky_data_in, &tricky_data_out, sizeof(TTrickyAlignedData)) == 0);
    assert(int32val == 20);

    assert(ptr32_array_in[0] == 0xABCDABCD);
    assert(ptr32_array_in[1] == 0xFEDCFEDC);
    assert(ptr32_array_in[2] == 0xFEEDBEEF);
    assert(ptr32_array_in[3] == 0xBEEFFEED);

    assert(!File::TestReadFile("test.tmp"));
}

#endif // _DEBUG
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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.

#define PRINT_INFO
//#define DEBUG
//#define DEBUG2

#include "virtionet.hpp"
#include <net/packet.hpp>
#include <kernel/irq_manager.hpp>
#include <kernel/syscalls.hpp>
#include <hw/pci.hpp>
#include <cstdlib>
#include <malloc.h>
#include <string.h>

//#define NO_DEFERRED_KICK
#ifndef NO_DEFERRED_KICK
static std::vector<VirtioNet*> deferred_devices;
static uint8_t deferred_intr;
#endif

using namespace net;

void VirtioNet::get_config() {
  Virtio::get_config(&_conf, _config_length);
}

VirtioNet::VirtioNet(hw::PCI_Device& d)
  : Virtio(d),
    Link(Link_protocol{{this, &VirtioNet::transmit}, mac()},
        std::max(2048u, queue_size(0) * 2), sizeof(net::Packet) + sizeof(virtio_net_hdr) + packet_len()),
    packets_rx_{Statman::get().create(Stat::UINT64, device_name() + ".packets_rx").get_uint64()},
    packets_tx_{Statman::get().create(Stat::UINT64, device_name() + ".packets_tx").get_uint64()}
{
  INFO("VirtioNet", "Driver initializing");
  // this must be true, otherwise packets will be created incorrectly
  assert(sizeof(virtio_net_hdr) <= sizeof(Packet));

  uint32_t needed_features = 0
    | (1 << VIRTIO_NET_F_MAC)
    | (1 << VIRTIO_NET_F_STATUS)
    ;//| (1 << VIRTIO_NET_F_MRG_RXBUF); //Merge RX Buffers (Everything i 1 buffer)
  uint32_t wanted_features = needed_features;
  negotiate_features(wanted_features);

  CHECK ((features() & needed_features) == needed_features,
         "Negotiated needed features");

  CHECK ((features() & wanted_features) == wanted_features,
         "Negotiated wanted features");

  CHECK(features() & (1 << VIRTIO_NET_F_CSUM),
        "Device handles packets w. partial checksum");

  CHECK(features() & (1 << VIRTIO_NET_F_GUEST_CSUM),
        "Guest handles packets w. partial checksum");

  CHECK(features() & (1 << VIRTIO_NET_F_CTRL_VQ),
        "There's a control queue");

  CHECK(features() & (1 << VIRTIO_F_ANY_LAYOUT),
        "Queue can handle any header/data layout");

  CHECK(features() & (1 << VIRTIO_F_RING_INDIRECT_DESC),
        "We can use indirect descriptors");

  CHECK(features() & (1 << VIRTIO_F_RING_EVENT_IDX),
        "There's a Ring Event Index to use");

  CHECK(features() & (1 << VIRTIO_NET_F_MQ),
        "There are multiple queue pairs");

  if (features() & (1 << VIRTIO_NET_F_MQ))
    printf("\t\t* max_virtqueue_pairs: 0x%x \n",_conf.max_virtq_pairs);

  CHECK(features() & (1 << VIRTIO_NET_F_MRG_RXBUF),
        "Merge RX buffers");

  /** RX que is 0, TX Queue is 1 - Virtio Std. §5.1.2  */
  new (&rx_q) Virtio::Queue(queue_size(0),0,iobase());
  new (&tx_q) Virtio::Queue(queue_size(1),1,iobase());
  new (&ctrl_q) Virtio::Queue(queue_size(2),2,iobase());

  // Step 1 - Initialize RX/TX queues
  auto success = assign_queue(0, (uint32_t)rx_q.queue_desc());
  CHECK(success, "RX queue (%u) assigned (0x%x) to device",
        rx_q.size(), (uint32_t)rx_q.queue_desc());

  success = assign_queue(1, (uint32_t)tx_q.queue_desc());
  CHECK(success, "TX queue (%u) assigned (0x%x) to device",
        tx_q.size(), (uint32_t)tx_q.queue_desc());

  // Step 2 - Initialize Ctrl-queue if it exists
  if (features() & (1 << VIRTIO_NET_F_CTRL_VQ)) {
    success = assign_queue(2, (uint32_t)tx_q.queue_desc());
    CHECK(success, "CTRL queue (%u) assigned (0x%x) to device",
          ctrl_q.size(), (uint32_t)ctrl_q.queue_desc());
  }

  // Step 3 - Fill receive queue with buffers
  // DEBUG: Disable
  INFO("VirtioNet", "Adding %u receive buffers of size %u",
       rx_q.size() / 2, bufstore().bufsize());

  for (int i = 0; i < rx_q.size() / 2; i++)
      add_receive_buffer(bufstore().get_buffer());

  // Step 4 - If there are many queues, we should negotiate the number.
  // Set config length, based on whether there are multiple queues
  if (features() & (1 << VIRTIO_NET_F_MQ))
    _config_length = sizeof(config);
  else
    _config_length = sizeof(config) - sizeof(uint16_t);
  // @todo: Specify how many queues we're going to use.

  // Step 5 - get the mac address (we're demanding this feature)
  // Step 6 - get the status - demanding this as well.
  // Getting the MAC + status
  get_config();

  CHECK(_conf.mac.major > 0, "Valid Mac address: %s",
        _conf.mac.str().c_str());

  // Step 7 - 9 - GSO: @todo Not using GSO features yet.

  // Signal setup complete.
  setup_complete((features() & needed_features) == needed_features);
  CHECK((features() & needed_features) == needed_features, "Signalled driver OK");

  // Hook up interrupts
  if (has_msix())
  {
    assert(get_msix_vectors() >= 3);
    // for now use service queues, otherwise stress test fails
    auto recv_del(delegate<void()>{this, &VirtioNet::msix_recv_handler});
    auto xmit_del(delegate<void()>{this, &VirtioNet::msix_xmit_handler});
    auto conf_del(delegate<void()>{this, &VirtioNet::msix_conf_handler});

    // update BSP IDT
    IRQ_manager::get().subscribe(irq() + 0, recv_del);
    IRQ_manager::get().subscribe(irq() + 1, xmit_del);
    IRQ_manager::get().subscribe(irq() + 2, conf_del);
  }
  else
  {
    assert(0 && "Legacy IRQs not supported");
  }

#ifndef NO_DEFERRED_KICK
  static bool init_deferred = false;
  if (!init_deferred) {
    init_deferred = true;
    deferred_intr = IRQ_manager::get().get_next_msix_irq();
    IRQ_manager::get().subscribe(deferred_intr, handle_deferred_devices);
  }
#endif

  // Done
  INFO("VirtioNet", "Driver initialization complete");
  CHECK(_conf.status & 1, "Link up\n");
  rx_q.kick();
}

void VirtioNet::msix_conf_handler()
{
  debug("\t <VirtioNet> Configuration change:\n");

  // Getting the MAC + status
  debug("\t             Old status: 0x%x\n",_conf.status);
  get_config();
  debug("\t             New status: 0x%x \n",_conf.status);
}
void VirtioNet::msix_recv_handler()
{
  bool dequeued_rx = false;
  rx_q.disable_interrupts();
  // handle incoming packets as long as bufstore has available buffers
  while (rx_q.new_incoming())
  {
    auto res = rx_q.dequeue();
    auto pckt_ptr = recv_packet(res.data(), res.size());
    Link::receive(std::move(pckt_ptr));

    // Requeue a new buffer
    if (bufstore().available() > 0)
        add_receive_buffer(bufstore().get_buffer());

    dequeued_rx = true;

    // Stat increase packets received
    packets_rx_++;
  }
  rx_q.enable_interrupts();
  if (dequeued_rx)
    rx_q.kick();
}
void VirtioNet::msix_xmit_handler()
{
  bool dequeued_tx = false;
  bool queued_rx   = false;
  tx_q.disable_interrupts();
  // Do one TX-packet
  while (tx_q.new_incoming())
  {
    auto res = tx_q.dequeue();

    // refill rx if needed
    if (rx_q.num_free() > 1) {
      add_receive_buffer(res.data() - sizeof(net::Packet));
      queued_rx = true;
    }
    else {
      // get packet offset, and call destructor
      auto* packet = (net::Packet*) (res.data() - sizeof(net::Packet));
      packet->~Packet(); // call destructor on Packet to release it
      dequeued_tx = true;
    }
  }

  // If we have a transmit queue, eat from it, otherwise let the stack know we
  // have increased transmit capacity
  if (dequeued_tx)
  {
    debug("<VirtioNet>%i dequeued, transmitting backlog\n", dequeued_tx);

    // transmit as much as possible from the buffer
    if (transmit_queue_) {
      transmit(std::move(transmit_queue_));
    }

    // If we now emptied the buffer, offer packets to stack
    if (!transmit_queue_ && tx_q.num_free() > 1)
        transmit_queue_available_event_(tx_q.num_free() / 2);
  }
  if (queued_rx) {
    rx_q.kick();
  }
}

void VirtioNet::add_receive_buffer(uint8_t* pkt)
{
  // offset pointer to virtionet header
  auto* vnet = pkt + sizeof(Packet);

  Token token1 {{vnet, sizeof(virtio_net_hdr)}, Token::IN };
  Token token2 {{vnet + sizeof(virtio_net_hdr), packet_len()}, Token::IN };

  std::array<Token, 2> tokens {{ token1, token2 }};
  rx_q.enqueue(tokens);
}

net::Packet_ptr
VirtioNet::recv_packet(uint8_t* data, uint16_t size)
{
  auto* ptr = (net::Packet*) (data - sizeof(net::Packet));
#ifdef DEBUG
  assert(bufstore().is_from_pool((uint8_t*) ptr));
  assert(bufstore().is_buffer((uint8_t*) ptr));
#endif
  new (ptr) net::Packet(packet_len(), size - sizeof(virtio_net_hdr), sizeof(virtio_net_hdr), &bufstore());
  return net::Packet_ptr(ptr);
}
net::Packet_ptr
VirtioNet::create_packet(uint16_t size)
{
  auto* ptr = (net::Packet*) bufstore().get_buffer();
  new (ptr) net::Packet(packet_len(), size, sizeof(virtio_net_hdr), &bufstore());
  return net::Packet_ptr(ptr);
}

void VirtioNet::add_to_tx_buffer(net::Packet_ptr pckt){
  if (transmit_queue_)
    transmit_queue_->chain(std::move(pckt));
  else
    transmit_queue_ = std::move(pckt);

#ifdef DEBUG
  size_t chain_length = 1;
  auto* next = transmit_queue_->tail();
  while (next) {
    chain_length++;
    next = next->tail();
  }
#endif

  debug("Buffering, %i packets chained \n", chain_length);
}

void VirtioNet::transmit(net::Packet_ptr pckt) {
  /** @note We have to send a virtio header first, then the packet.

      From Virtio std. §5.1.6.6:
      "When using legacy interfaces, transitional drivers which have not
      negotiated VIRTIO_F_ANY_LAYOUT MUST use a single descriptor for the struct
      virtio_net_hdr on both transmit and receive, with the network data in the
      following descriptors."

      VirtualBox *does not* accept ANY_LAYOUT, while Qemu does, so this is to
      support VirtualBox
  */
  int transmitted = 0;
  net::Packet_ptr tail = std::move(pckt);

  // Transmit all we can directly
  while (tx_q.num_free() and tail) {
    debug("%i tokens left in TX queue \n", tx_q.num_free());
    // next in line
    auto next = tail->detach_tail();
    // write data to network
    // explicitly release the data to prevent destructor being called
    enqueue(tail.release());
    tail = std::move(next);
    transmitted++;
    // Stat increase packets transmitted
    packets_tx_++;

    if (!tail) break;
  }

  // Notify virtio about new packets
  if (LIKELY(transmitted)) {
    begin_deferred_kick();
  }

  // Buffer the rest
  if (UNLIKELY(tail)) {
    debug("Buffering remaining packets..\n");
    add_to_tx_buffer(std::move(tail));
  }
}

void VirtioNet::enqueue(net::Packet* pckt)
{
  auto* hdr = pckt->buffer() - sizeof(virtio_net_hdr);

  Token token1 {{ hdr, sizeof(virtio_net_hdr)}, Token::OUT };
  Token token2 {{ pckt->buffer(), pckt->size()}, Token::OUT };

  std::array<Token, 2> tokens {{ token1, token2 }};

  // Enqueue scatterlist, 2 pieces readable, 0 writable.
  tx_q.enqueue(tokens);
}

void VirtioNet::begin_deferred_kick()
{
#ifdef NO_DEFERRED_KICK
  tx_q.enable_interrupts();
  tx_q.kick();
#else
  if (!deferred_kick) {
    deferred_kick = true;
    deferred_devices.push_back(this);
    IRQ_manager::get().register_irq(deferred_intr);
  }
#endif
}
void VirtioNet::handle_deferred_devices()
{
#ifndef NO_DEFERRED_KICK
  for (auto* dev : deferred_devices) {
    // kick transmitq
    dev->deferred_kick = false;
    dev->tx_q.enable_interrupts();
    dev->tx_q.kick();
  }
  deferred_devices.clear();
#endif
}

void VirtioNet::deactivate()
{
  /// disable interrupts on virtio queues
  rx_q.disable_interrupts();
  tx_q.disable_interrupts();
  ctrl_q.disable_interrupts();

  /// mask off MSI-X vectors
  if (has_msix())
      deactivate_msix();
}

#include <kernel/pci_manager.hpp>

/** Register VirtioNet's driver factory at the PCI_manager */
static struct Autoreg_virtionet {
  Autoreg_virtionet() {
    PCI_manager::register_driver<hw::Nic>(hw::PCI_Device::VENDOR_VIRTIO, 0x1000, &VirtioNet::new_instance);
  }
} autoreg_virtionet;
<commit_msg>virtionet: Undo refilling rxq, as it would have no effect when routing<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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.

#define PRINT_INFO
//#define DEBUG
//#define DEBUG2

#include "virtionet.hpp"
#include <net/packet.hpp>
#include <kernel/irq_manager.hpp>
#include <kernel/syscalls.hpp>
#include <hw/pci.hpp>
#include <cstdlib>
#include <malloc.h>
#include <string.h>

//#define NO_DEFERRED_KICK
#ifndef NO_DEFERRED_KICK
static std::vector<VirtioNet*> deferred_devices;
static uint8_t deferred_intr;
#endif

using namespace net;

void VirtioNet::get_config() {
  Virtio::get_config(&_conf, _config_length);
}

VirtioNet::VirtioNet(hw::PCI_Device& d)
  : Virtio(d),
    Link(Link_protocol{{this, &VirtioNet::transmit}, mac()},
        std::max(2048u, queue_size(0) * 2), sizeof(net::Packet) + sizeof(virtio_net_hdr) + packet_len()),
    packets_rx_{Statman::get().create(Stat::UINT64, device_name() + ".packets_rx").get_uint64()},
    packets_tx_{Statman::get().create(Stat::UINT64, device_name() + ".packets_tx").get_uint64()}
{
  INFO("VirtioNet", "Driver initializing");
  // this must be true, otherwise packets will be created incorrectly
  assert(sizeof(virtio_net_hdr) <= sizeof(Packet));

  uint32_t needed_features = 0
    | (1 << VIRTIO_NET_F_MAC)
    | (1 << VIRTIO_NET_F_STATUS)
    ;//| (1 << VIRTIO_NET_F_MRG_RXBUF); //Merge RX Buffers (Everything i 1 buffer)
  uint32_t wanted_features = needed_features;
  negotiate_features(wanted_features);

  CHECK ((features() & needed_features) == needed_features,
         "Negotiated needed features");

  CHECK ((features() & wanted_features) == wanted_features,
         "Negotiated wanted features");

  CHECK(features() & (1 << VIRTIO_NET_F_CSUM),
        "Device handles packets w. partial checksum");

  CHECK(features() & (1 << VIRTIO_NET_F_GUEST_CSUM),
        "Guest handles packets w. partial checksum");

  CHECK(features() & (1 << VIRTIO_NET_F_CTRL_VQ),
        "There's a control queue");

  CHECK(features() & (1 << VIRTIO_F_ANY_LAYOUT),
        "Queue can handle any header/data layout");

  CHECK(features() & (1 << VIRTIO_F_RING_INDIRECT_DESC),
        "We can use indirect descriptors");

  CHECK(features() & (1 << VIRTIO_F_RING_EVENT_IDX),
        "There's a Ring Event Index to use");

  CHECK(features() & (1 << VIRTIO_NET_F_MQ),
        "There are multiple queue pairs");

  if (features() & (1 << VIRTIO_NET_F_MQ))
    printf("\t\t* max_virtqueue_pairs: 0x%x \n",_conf.max_virtq_pairs);

  CHECK(features() & (1 << VIRTIO_NET_F_MRG_RXBUF),
        "Merge RX buffers");

  /** RX que is 0, TX Queue is 1 - Virtio Std. §5.1.2  */
  new (&rx_q) Virtio::Queue(queue_size(0),0,iobase());
  new (&tx_q) Virtio::Queue(queue_size(1),1,iobase());
  new (&ctrl_q) Virtio::Queue(queue_size(2),2,iobase());

  // Step 1 - Initialize RX/TX queues
  auto success = assign_queue(0, (uint32_t)rx_q.queue_desc());
  CHECK(success, "RX queue (%u) assigned (0x%x) to device",
        rx_q.size(), (uint32_t)rx_q.queue_desc());

  success = assign_queue(1, (uint32_t)tx_q.queue_desc());
  CHECK(success, "TX queue (%u) assigned (0x%x) to device",
        tx_q.size(), (uint32_t)tx_q.queue_desc());

  // Step 2 - Initialize Ctrl-queue if it exists
  if (features() & (1 << VIRTIO_NET_F_CTRL_VQ)) {
    success = assign_queue(2, (uint32_t)tx_q.queue_desc());
    CHECK(success, "CTRL queue (%u) assigned (0x%x) to device",
          ctrl_q.size(), (uint32_t)ctrl_q.queue_desc());
  }

  // Step 3 - Fill receive queue with buffers
  // DEBUG: Disable
  INFO("VirtioNet", "Adding %u receive buffers of size %u",
       rx_q.size() / 2, bufstore().bufsize());

  for (int i = 0; i < rx_q.size() / 2; i++)
      add_receive_buffer(bufstore().get_buffer());

  // Step 4 - If there are many queues, we should negotiate the number.
  // Set config length, based on whether there are multiple queues
  if (features() & (1 << VIRTIO_NET_F_MQ))
    _config_length = sizeof(config);
  else
    _config_length = sizeof(config) - sizeof(uint16_t);
  // @todo: Specify how many queues we're going to use.

  // Step 5 - get the mac address (we're demanding this feature)
  // Step 6 - get the status - demanding this as well.
  // Getting the MAC + status
  get_config();

  CHECK(_conf.mac.major > 0, "Valid Mac address: %s",
        _conf.mac.str().c_str());

  // Step 7 - 9 - GSO: @todo Not using GSO features yet.

  // Signal setup complete.
  setup_complete((features() & needed_features) == needed_features);
  CHECK((features() & needed_features) == needed_features, "Signalled driver OK");

  // Hook up interrupts
  if (has_msix())
  {
    assert(get_msix_vectors() >= 3);
    // for now use service queues, otherwise stress test fails
    auto recv_del(delegate<void()>{this, &VirtioNet::msix_recv_handler});
    auto xmit_del(delegate<void()>{this, &VirtioNet::msix_xmit_handler});
    auto conf_del(delegate<void()>{this, &VirtioNet::msix_conf_handler});

    // update BSP IDT
    IRQ_manager::get().subscribe(irq() + 0, recv_del);
    IRQ_manager::get().subscribe(irq() + 1, xmit_del);
    IRQ_manager::get().subscribe(irq() + 2, conf_del);
  }
  else
  {
    assert(0 && "Legacy IRQs not supported");
  }

#ifndef NO_DEFERRED_KICK
  static bool init_deferred = false;
  if (!init_deferred) {
    init_deferred = true;
    deferred_intr = IRQ_manager::get().get_next_msix_irq();
    IRQ_manager::get().subscribe(deferred_intr, handle_deferred_devices);
  }
#endif

  // Done
  INFO("VirtioNet", "Driver initialization complete");
  CHECK(_conf.status & 1, "Link up\n");
  rx_q.kick();
}

void VirtioNet::msix_conf_handler()
{
  debug("\t <VirtioNet> Configuration change:\n");

  // Getting the MAC + status
  debug("\t             Old status: 0x%x\n",_conf.status);
  get_config();
  debug("\t             New status: 0x%x \n",_conf.status);
}
void VirtioNet::msix_recv_handler()
{
  bool dequeued_rx = false;
  rx_q.disable_interrupts();
  // handle incoming packets as long as bufstore has available buffers
  while (rx_q.new_incoming())
  {
    auto res = rx_q.dequeue();
    auto pckt_ptr = recv_packet(res.data(), res.size());
    Link::receive(std::move(pckt_ptr));

    // Requeue a new buffer
    if (bufstore().available() > 0)
        add_receive_buffer(bufstore().get_buffer());

    dequeued_rx = true;

    // Stat increase packets received
    packets_rx_++;
  }
  rx_q.enable_interrupts();
  if (dequeued_rx)
    rx_q.kick();
}
void VirtioNet::msix_xmit_handler()
{
  bool dequeued_tx = false;
  tx_q.disable_interrupts();
  // Do one TX-packet
  while (tx_q.new_incoming())
  {
    auto res = tx_q.dequeue();

    // get packet offset, and call destructor
    auto* packet = (net::Packet*) (res.data() - sizeof(net::Packet));
    packet->~Packet(); // call destructor on Packet to release it
    dequeued_tx = true;
  }

  // If we have a transmit queue, eat from it, otherwise let the stack know we
  // have increased transmit capacity
  if (dequeued_tx)
  {
    debug("<VirtioNet>%i dequeued, transmitting backlog\n", dequeued_tx);

    // transmit as much as possible from the buffer
    if (transmit_queue_) {
      transmit(std::move(transmit_queue_));
    }

    // If we now emptied the buffer, offer packets to stack
    if (!transmit_queue_ && tx_q.num_free() > 1)
        transmit_queue_available_event_(tx_q.num_free() / 2);
  }
}

void VirtioNet::add_receive_buffer(uint8_t* pkt)
{
  // offset pointer to virtionet header
  auto* vnet = pkt + sizeof(Packet);

  Token token1 {{vnet, sizeof(virtio_net_hdr)}, Token::IN };
  Token token2 {{vnet + sizeof(virtio_net_hdr), packet_len()}, Token::IN };

  std::array<Token, 2> tokens {{ token1, token2 }};
  rx_q.enqueue(tokens);
}

net::Packet_ptr
VirtioNet::recv_packet(uint8_t* data, uint16_t size)
{
  auto* ptr = (net::Packet*) (data - sizeof(net::Packet));
#ifdef DEBUG
  assert(bufstore().is_from_pool((uint8_t*) ptr));
  assert(bufstore().is_buffer((uint8_t*) ptr));
#endif
  new (ptr) net::Packet(packet_len(), size - sizeof(virtio_net_hdr), sizeof(virtio_net_hdr), &bufstore());
  return net::Packet_ptr(ptr);
}
net::Packet_ptr
VirtioNet::create_packet(uint16_t size)
{
  auto* ptr = (net::Packet*) bufstore().get_buffer();
  new (ptr) net::Packet(packet_len(), size, sizeof(virtio_net_hdr), &bufstore());
  return net::Packet_ptr(ptr);
}

void VirtioNet::add_to_tx_buffer(net::Packet_ptr pckt){
  if (transmit_queue_)
    transmit_queue_->chain(std::move(pckt));
  else
    transmit_queue_ = std::move(pckt);

#ifdef DEBUG
  size_t chain_length = 1;
  auto* next = transmit_queue_->tail();
  while (next) {
    chain_length++;
    next = next->tail();
  }
#endif

  debug("Buffering, %i packets chained \n", chain_length);
}

void VirtioNet::transmit(net::Packet_ptr pckt) {
  /** @note We have to send a virtio header first, then the packet.

      From Virtio std. §5.1.6.6:
      "When using legacy interfaces, transitional drivers which have not
      negotiated VIRTIO_F_ANY_LAYOUT MUST use a single descriptor for the struct
      virtio_net_hdr on both transmit and receive, with the network data in the
      following descriptors."

      VirtualBox *does not* accept ANY_LAYOUT, while Qemu does, so this is to
      support VirtualBox
  */
  int transmitted = 0;
  net::Packet_ptr tail = std::move(pckt);

  // Transmit all we can directly
  while (tx_q.num_free() and tail) {
    debug("%i tokens left in TX queue \n", tx_q.num_free());
    // next in line
    auto next = tail->detach_tail();
    // write data to network
    // explicitly release the data to prevent destructor being called
    enqueue(tail.release());
    tail = std::move(next);
    transmitted++;
    // Stat increase packets transmitted
    packets_tx_++;

    if (!tail) break;
  }

  // Notify virtio about new packets
  if (LIKELY(transmitted)) {
    begin_deferred_kick();
  }

  // Buffer the rest
  if (UNLIKELY(tail)) {
    debug("Buffering remaining packets..\n");
    add_to_tx_buffer(std::move(tail));
  }
}

void VirtioNet::enqueue(net::Packet* pckt)
{
  auto* hdr = pckt->buffer() - sizeof(virtio_net_hdr);

  Token token1 {{ hdr, sizeof(virtio_net_hdr)}, Token::OUT };
  Token token2 {{ pckt->buffer(), pckt->size()}, Token::OUT };

  std::array<Token, 2> tokens {{ token1, token2 }};

  // Enqueue scatterlist, 2 pieces readable, 0 writable.
  tx_q.enqueue(tokens);
}

void VirtioNet::begin_deferred_kick()
{
#ifdef NO_DEFERRED_KICK
  tx_q.enable_interrupts();
  tx_q.kick();
#else
  if (!deferred_kick) {
    deferred_kick = true;
    deferred_devices.push_back(this);
    IRQ_manager::get().register_irq(deferred_intr);
  }
#endif
}
void VirtioNet::handle_deferred_devices()
{
#ifndef NO_DEFERRED_KICK
  for (auto* dev : deferred_devices) {
    // kick transmitq
    dev->deferred_kick = false;
    dev->tx_q.enable_interrupts();
    dev->tx_q.kick();
  }
  deferred_devices.clear();
#endif
}

void VirtioNet::deactivate()
{
  /// disable interrupts on virtio queues
  rx_q.disable_interrupts();
  tx_q.disable_interrupts();
  ctrl_q.disable_interrupts();

  /// mask off MSI-X vectors
  if (has_msix())
      deactivate_msix();
}

#include <kernel/pci_manager.hpp>

/** Register VirtioNet's driver factory at the PCI_manager */
static struct Autoreg_virtionet {
  Autoreg_virtionet() {
    PCI_manager::register_driver<hw::Nic>(hw::PCI_Device::VENDOR_VIRTIO, 0x1000, &VirtioNet::new_instance);
  }
} autoreg_virtionet;
<|endoftext|>


<commit_before>//===-- X86ATTAsmPrinter.cpp - Convert X86 LLVM code to Intel assembly ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains a printer that converts from our internal representation
// of machine-dependent LLVM code to AT&T format assembly
// language. This printer is the output mechanism used by `llc'.
//
//===----------------------------------------------------------------------===//

#include "X86ATTAsmPrinter.h"
#include "X86.h"
#include "X86TargetMachine.h"
#include "llvm/Module.h"
#include "llvm/Support/Mangler.h"
#include "llvm/Target/TargetOptions.h"
#include <iostream>
using namespace llvm;
using namespace x86;

/// runOnMachineFunction - This uses the printMachineInstruction()
/// method to print assembly for each instruction.
///
bool X86ATTAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
  SetupMachineFunction(MF);
  O << "\n\n";

  // Print out constants referenced by the function
  EmitConstantPool(MF.getConstantPool());

  // Print out labels for the function.
  const Function *F = MF.getFunction();
  switch (F->getLinkage()) {
  default: assert(0 && "Unknown linkage type!");
  case Function::InternalLinkage:  // Symbols default to internal.
    SwitchSection(".text", F);
    EmitAlignment(4, F);     // FIXME: This should be parameterized somewhere.
    break;
  case Function::ExternalLinkage:
    SwitchSection(".text", F);
    EmitAlignment(4, F);     // FIXME: This should be parameterized somewhere.
    O << "\t.globl\t" << CurrentFnName << "\n";
    break;
  case Function::WeakLinkage:
  case Function::LinkOnceLinkage:
    if (forDarwin) {
      SwitchSection(".section __TEXT,__textcoal_nt,coalesced,pure_instructions",
                    F);
      O << "\t.weak_definition\t" << CurrentFnName << "\n";
    } else {
      EmitAlignment(4, F);     // FIXME: This should be parameterized somewhere.
      O << "\t.section\t.llvm.linkonce.t." << CurrentFnName
        << ",\"ax\",@progbits\n";
      O << "\t.weak " << CurrentFnName << "\n";
    }
    break;
  }
  O << CurrentFnName << ":\n";

  // Print out code for the function.
  for (MachineFunction::const_iterator I = MF.begin(), E = MF.end();
       I != E; ++I) {
    // Print a label for the basic block.
    if (I->pred_begin() != I->pred_end())
      O << PrivateGlobalPrefix << "BB" << CurrentFnName << "_" << I->getNumber()
        << ":\t" << CommentString << " " << I->getBasicBlock()->getName()
        << "\n";
    for (MachineBasicBlock::const_iterator II = I->begin(), E = I->end();
         II != E; ++II) {
      // Print the assembly for the instruction.
      O << "\t";
      printMachineInstruction(II);
    }
  }
  if (HasDotTypeDotSizeDirective)
    O << "\t.size " << CurrentFnName << ", .-" << CurrentFnName << "\n";

  // We didn't modify anything.
  return false;
}

void X86ATTAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo,
                                    const char *Modifier) {
  const MachineOperand &MO = MI->getOperand(OpNo);
  const MRegisterInfo &RI = *TM.getRegisterInfo();
  switch (MO.getType()) {
  case MachineOperand::MO_VirtualRegister:
  case MachineOperand::MO_MachineRegister:
    assert(MRegisterInfo::isPhysicalRegister(MO.getReg()) &&
           "Virtual registers should not make it this far!");
    O << '%';
    for (const char *Name = RI.get(MO.getReg()).Name; *Name; ++Name)
      O << (char)tolower(*Name);
    return;

  case MachineOperand::MO_SignExtendedImmed:
  case MachineOperand::MO_UnextendedImmed:
    O << '$' << (int)MO.getImmedValue();
    return;
  case MachineOperand::MO_MachineBasicBlock: {
    MachineBasicBlock *MBBOp = MO.getMachineBasicBlock();
    O << PrivateGlobalPrefix << "BB"
      << Mang->getValueName(MBBOp->getParent()->getFunction())
      << "_" << MBBOp->getNumber () << "\t# "
      << MBBOp->getBasicBlock ()->getName ();
    return;
  }
  case MachineOperand::MO_PCRelativeDisp:
    std::cerr << "Shouldn't use addPCDisp() when building X86 MachineInstrs";
    abort ();
    return;
  case MachineOperand::MO_GlobalAddress: {
    bool isCallOp = Modifier && !strcmp(Modifier, "call");
    bool isMemOp  = Modifier && !strcmp(Modifier, "mem");
    if (!isMemOp && !isCallOp) O << '$';
    // Darwin block shameless ripped from PPCAsmPrinter.cpp
    if (forDarwin && TM.getRelocationModel() != Reloc::Static) {
      GlobalValue *GV = MO.getGlobal();
      std::string Name = Mang->getValueName(GV);
      // Link-once, External, or Weakly-linked global variables need
      // non-lazily-resolved stubs
      if (GV->isExternal() || GV->hasWeakLinkage() ||
          GV->hasLinkOnceLinkage()) {
        // Dynamically-resolved functions need a stub for the function.
        if (isCallOp && isa<Function>(GV) && cast<Function>(GV)->isExternal()) {
          FnStubs.insert(Name);
          O << "L" << Name << "$stub";
        } else {
          GVStubs.insert(Name);
          O << "L" << Name << "$non_lazy_ptr";
          if (TM.getRelocationModel() == Reloc::PIC)
            O << "-\"L" << getFunctionNumber() << "$pb\"";
        }
      } else {
        O << Mang->getValueName(GV);
      }
    } else
      O << Mang->getValueName(MO.getGlobal());
    int Offset = MO.getOffset();
    if (Offset > 0)
      O << "+" << Offset;
    else if (Offset < 0)
      O << Offset;
    return;
  }
  case MachineOperand::MO_ExternalSymbol: {
    bool isCallOp = Modifier && !strcmp(Modifier, "call");
    if (isCallOp && forDarwin && TM.getRelocationModel() != Reloc::Static) {
      std::string Name(GlobalPrefix);
      Name += MO.getSymbolName();
      FnStubs.insert(Name);
      O << "L" << Name << "$stub";
      return;
    }
    if (!isCallOp) O << '$';
    O << GlobalPrefix << MO.getSymbolName();
    return;
  }
  default:
    O << "<unknown operand type>"; return;
  }
}

void X86ATTAsmPrinter::printSSECC(const MachineInstr *MI, unsigned Op) {
  unsigned char value = MI->getOperand(Op).getImmedValue();
  assert(value <= 7 && "Invalid ssecc argument!");
  switch (value) {
  case 0: O << "eq"; break;
  case 1: O << "lt"; break;
  case 2: O << "le"; break;
  case 3: O << "unord"; break;
  case 4: O << "neq"; break;
  case 5: O << "nlt"; break;
  case 6: O << "nle"; break;
  case 7: O << "ord"; break;
  }
}

void X86ATTAsmPrinter::printMemReference(const MachineInstr *MI, unsigned Op){
  assert(isMem(MI, Op) && "Invalid memory reference!");

  const MachineOperand &BaseReg  = MI->getOperand(Op);
  int ScaleVal                   = MI->getOperand(Op+1).getImmedValue();
  const MachineOperand &IndexReg = MI->getOperand(Op+2);
  const MachineOperand &DispSpec = MI->getOperand(Op+3);

  if (BaseReg.isFrameIndex()) {
    O << "[frame slot #" << BaseReg.getFrameIndex();
    if (DispSpec.getImmedValue())
      O << " + " << DispSpec.getImmedValue();
    O << "]";
    return;
  } else if (BaseReg.isConstantPoolIndex()) {
    O << PrivateGlobalPrefix << "CPI" << getFunctionNumber() << "_"
      << BaseReg.getConstantPoolIndex();
    if (forDarwin && TM.getRelocationModel() == Reloc::PIC)
      O << "-\"L" << getFunctionNumber() << "$pb\"";
    if (DispSpec.getImmedValue())
      O << "+" << DispSpec.getImmedValue();
    if (IndexReg.getReg()) {
      O << "(,";
      printOperand(MI, Op+2);
      if (ScaleVal != 1)
        O << "," << ScaleVal;
      O << ")";
    }
    return;
  }

  if (DispSpec.isGlobalAddress()) {
    printOperand(MI, Op+3, "mem");
  } else {
    int DispVal = DispSpec.getImmedValue();
    if (DispVal || (!IndexReg.getReg() && !BaseReg.getReg()))
      O << DispVal;
  }

  if (IndexReg.getReg() || BaseReg.getReg()) {
    O << "(";
    if (BaseReg.getReg())
      printOperand(MI, Op);

    if (IndexReg.getReg()) {
      O << ",";
      printOperand(MI, Op+2);
      if (ScaleVal != 1)
        O << "," << ScaleVal;
    }

    O << ")";
  }
}

void X86ATTAsmPrinter::printPICLabel(const MachineInstr *MI, unsigned Op) {
  O << "\"L" << getFunctionNumber() << "$pb\"\n";
  O << "\"L" << getFunctionNumber() << "$pb\":";
}

/// printMachineInstruction -- Print out a single X86 LLVM instruction
/// MI in Intel syntax to the current output stream.
///
void X86ATTAsmPrinter::printMachineInstruction(const MachineInstr *MI) {
  ++EmittedInsts;
  // This works around some Darwin assembler bugs.
  if (forDarwin) {
    switch (MI->getOpcode()) {
    case X86::REP_MOVSB:
      O << "rep/movsb (%esi),(%edi)\n";
      return;
    case X86::REP_MOVSD:
      O << "rep/movsl (%esi),(%edi)\n";
      return;
    case X86::REP_MOVSW:
      O << "rep/movsw (%esi),(%edi)\n";
      return;
    case X86::REP_STOSB:
      O << "rep/stosb\n";
      return;
    case X86::REP_STOSD:
      O << "rep/stosl\n";
      return;
    case X86::REP_STOSW:
      O << "rep/stosw\n";
      return;
    default:
      break;
    }
  }

  // Call the autogenerated instruction printer routines.
  printInstruction(MI);
}

// Include the auto-generated portion of the assembly writer.
#include "X86GenAsmWriter.inc"

<commit_msg>Missing .globl for weak / link-once .text symbols.<commit_after>//===-- X86ATTAsmPrinter.cpp - Convert X86 LLVM code to Intel assembly ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains a printer that converts from our internal representation
// of machine-dependent LLVM code to AT&T format assembly
// language. This printer is the output mechanism used by `llc'.
//
//===----------------------------------------------------------------------===//

#include "X86ATTAsmPrinter.h"
#include "X86.h"
#include "X86TargetMachine.h"
#include "llvm/Module.h"
#include "llvm/Support/Mangler.h"
#include "llvm/Target/TargetOptions.h"
#include <iostream>
using namespace llvm;
using namespace x86;

/// runOnMachineFunction - This uses the printMachineInstruction()
/// method to print assembly for each instruction.
///
bool X86ATTAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
  SetupMachineFunction(MF);
  O << "\n\n";

  // Print out constants referenced by the function
  EmitConstantPool(MF.getConstantPool());

  // Print out labels for the function.
  const Function *F = MF.getFunction();
  switch (F->getLinkage()) {
  default: assert(0 && "Unknown linkage type!");
  case Function::InternalLinkage:  // Symbols default to internal.
    SwitchSection(".text", F);
    EmitAlignment(4, F);     // FIXME: This should be parameterized somewhere.
    break;
  case Function::ExternalLinkage:
    SwitchSection(".text", F);
    EmitAlignment(4, F);     // FIXME: This should be parameterized somewhere.
    O << "\t.globl\t" << CurrentFnName << "\n";
    break;
  case Function::WeakLinkage:
  case Function::LinkOnceLinkage:
    if (forDarwin) {
      SwitchSection(".section __TEXT,__textcoal_nt,coalesced,pure_instructions",
                    F);
      O << "\t.globl\t" << CurrentFnName << "\n";
      O << "\t.weak_definition\t" << CurrentFnName << "\n";
    } else {
      EmitAlignment(4, F);     // FIXME: This should be parameterized somewhere.
      O << "\t.section\t.llvm.linkonce.t." << CurrentFnName
        << ",\"ax\",@progbits\n";
      O << "\t.weak " << CurrentFnName << "\n";
    }
    break;
  }
  O << CurrentFnName << ":\n";

  // Print out code for the function.
  for (MachineFunction::const_iterator I = MF.begin(), E = MF.end();
       I != E; ++I) {
    // Print a label for the basic block.
    if (I->pred_begin() != I->pred_end())
      O << PrivateGlobalPrefix << "BB" << CurrentFnName << "_" << I->getNumber()
        << ":\t" << CommentString << " " << I->getBasicBlock()->getName()
        << "\n";
    for (MachineBasicBlock::const_iterator II = I->begin(), E = I->end();
         II != E; ++II) {
      // Print the assembly for the instruction.
      O << "\t";
      printMachineInstruction(II);
    }
  }
  if (HasDotTypeDotSizeDirective)
    O << "\t.size " << CurrentFnName << ", .-" << CurrentFnName << "\n";

  // We didn't modify anything.
  return false;
}

void X86ATTAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo,
                                    const char *Modifier) {
  const MachineOperand &MO = MI->getOperand(OpNo);
  const MRegisterInfo &RI = *TM.getRegisterInfo();
  switch (MO.getType()) {
  case MachineOperand::MO_VirtualRegister:
  case MachineOperand::MO_MachineRegister:
    assert(MRegisterInfo::isPhysicalRegister(MO.getReg()) &&
           "Virtual registers should not make it this far!");
    O << '%';
    for (const char *Name = RI.get(MO.getReg()).Name; *Name; ++Name)
      O << (char)tolower(*Name);
    return;

  case MachineOperand::MO_SignExtendedImmed:
  case MachineOperand::MO_UnextendedImmed:
    O << '$' << (int)MO.getImmedValue();
    return;
  case MachineOperand::MO_MachineBasicBlock: {
    MachineBasicBlock *MBBOp = MO.getMachineBasicBlock();
    O << PrivateGlobalPrefix << "BB"
      << Mang->getValueName(MBBOp->getParent()->getFunction())
      << "_" << MBBOp->getNumber () << "\t# "
      << MBBOp->getBasicBlock ()->getName ();
    return;
  }
  case MachineOperand::MO_PCRelativeDisp:
    std::cerr << "Shouldn't use addPCDisp() when building X86 MachineInstrs";
    abort ();
    return;
  case MachineOperand::MO_GlobalAddress: {
    bool isCallOp = Modifier && !strcmp(Modifier, "call");
    bool isMemOp  = Modifier && !strcmp(Modifier, "mem");
    if (!isMemOp && !isCallOp) O << '$';
    // Darwin block shameless ripped from PPCAsmPrinter.cpp
    if (forDarwin && TM.getRelocationModel() != Reloc::Static) {
      GlobalValue *GV = MO.getGlobal();
      std::string Name = Mang->getValueName(GV);
      // Link-once, External, or Weakly-linked global variables need
      // non-lazily-resolved stubs
      if (GV->isExternal() || GV->hasWeakLinkage() ||
          GV->hasLinkOnceLinkage()) {
        // Dynamically-resolved functions need a stub for the function.
        if (isCallOp && isa<Function>(GV) && cast<Function>(GV)->isExternal()) {
          FnStubs.insert(Name);
          O << "L" << Name << "$stub";
        } else {
          GVStubs.insert(Name);
          O << "L" << Name << "$non_lazy_ptr";
          if (TM.getRelocationModel() == Reloc::PIC)
            O << "-\"L" << getFunctionNumber() << "$pb\"";
        }
      } else {
        O << Mang->getValueName(GV);
      }
    } else
      O << Mang->getValueName(MO.getGlobal());
    int Offset = MO.getOffset();
    if (Offset > 0)
      O << "+" << Offset;
    else if (Offset < 0)
      O << Offset;
    return;
  }
  case MachineOperand::MO_ExternalSymbol: {
    bool isCallOp = Modifier && !strcmp(Modifier, "call");
    if (isCallOp && forDarwin && TM.getRelocationModel() != Reloc::Static) {
      std::string Name(GlobalPrefix);
      Name += MO.getSymbolName();
      FnStubs.insert(Name);
      O << "L" << Name << "$stub";
      return;
    }
    if (!isCallOp) O << '$';
    O << GlobalPrefix << MO.getSymbolName();
    return;
  }
  default:
    O << "<unknown operand type>"; return;
  }
}

void X86ATTAsmPrinter::printSSECC(const MachineInstr *MI, unsigned Op) {
  unsigned char value = MI->getOperand(Op).getImmedValue();
  assert(value <= 7 && "Invalid ssecc argument!");
  switch (value) {
  case 0: O << "eq"; break;
  case 1: O << "lt"; break;
  case 2: O << "le"; break;
  case 3: O << "unord"; break;
  case 4: O << "neq"; break;
  case 5: O << "nlt"; break;
  case 6: O << "nle"; break;
  case 7: O << "ord"; break;
  }
}

void X86ATTAsmPrinter::printMemReference(const MachineInstr *MI, unsigned Op){
  assert(isMem(MI, Op) && "Invalid memory reference!");

  const MachineOperand &BaseReg  = MI->getOperand(Op);
  int ScaleVal                   = MI->getOperand(Op+1).getImmedValue();
  const MachineOperand &IndexReg = MI->getOperand(Op+2);
  const MachineOperand &DispSpec = MI->getOperand(Op+3);

  if (BaseReg.isFrameIndex()) {
    O << "[frame slot #" << BaseReg.getFrameIndex();
    if (DispSpec.getImmedValue())
      O << " + " << DispSpec.getImmedValue();
    O << "]";
    return;
  } else if (BaseReg.isConstantPoolIndex()) {
    O << PrivateGlobalPrefix << "CPI" << getFunctionNumber() << "_"
      << BaseReg.getConstantPoolIndex();
    if (forDarwin && TM.getRelocationModel() == Reloc::PIC)
      O << "-\"L" << getFunctionNumber() << "$pb\"";
    if (DispSpec.getImmedValue())
      O << "+" << DispSpec.getImmedValue();
    if (IndexReg.getReg()) {
      O << "(,";
      printOperand(MI, Op+2);
      if (ScaleVal != 1)
        O << "," << ScaleVal;
      O << ")";
    }
    return;
  }

  if (DispSpec.isGlobalAddress()) {
    printOperand(MI, Op+3, "mem");
  } else {
    int DispVal = DispSpec.getImmedValue();
    if (DispVal || (!IndexReg.getReg() && !BaseReg.getReg()))
      O << DispVal;
  }

  if (IndexReg.getReg() || BaseReg.getReg()) {
    O << "(";
    if (BaseReg.getReg())
      printOperand(MI, Op);

    if (IndexReg.getReg()) {
      O << ",";
      printOperand(MI, Op+2);
      if (ScaleVal != 1)
        O << "," << ScaleVal;
    }

    O << ")";
  }
}

void X86ATTAsmPrinter::printPICLabel(const MachineInstr *MI, unsigned Op) {
  O << "\"L" << getFunctionNumber() << "$pb\"\n";
  O << "\"L" << getFunctionNumber() << "$pb\":";
}

/// printMachineInstruction -- Print out a single X86 LLVM instruction
/// MI in Intel syntax to the current output stream.
///
void X86ATTAsmPrinter::printMachineInstruction(const MachineInstr *MI) {
  ++EmittedInsts;
  // This works around some Darwin assembler bugs.
  if (forDarwin) {
    switch (MI->getOpcode()) {
    case X86::REP_MOVSB:
      O << "rep/movsb (%esi),(%edi)\n";
      return;
    case X86::REP_MOVSD:
      O << "rep/movsl (%esi),(%edi)\n";
      return;
    case X86::REP_MOVSW:
      O << "rep/movsw (%esi),(%edi)\n";
      return;
    case X86::REP_STOSB:
      O << "rep/stosb\n";
      return;
    case X86::REP_STOSD:
      O << "rep/stosl\n";
      return;
    case X86::REP_STOSW:
      O << "rep/stosw\n";
      return;
    default:
      break;
    }
  }

  // Call the autogenerated instruction printer routines.
  printInstruction(MI);
}

// Include the auto-generated portion of the assembly writer.
#include "X86GenAsmWriter.inc"

<|endoftext|>


<commit_before>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef SIGNAL_EVENT_HXX
#define SIGNAL_EVENT_HXX

#include "Event.hxx"

class SignalEvent {
    Event event;

public:
    SignalEvent() = default;

    SignalEvent(int sig, event_callback_fn callback, void *ctx) {
        Set(sig, callback, ctx);
    }

    void Set(int sig, event_callback_fn callback, void *ctx) {
        event.SetSignal(sig, callback, ctx);
    }

    void Add(const struct timeval *timeout=nullptr) {
        event.Add(timeout);
    }

    void Delete() {
        event.Delete();
    }
};

#endif
<commit_msg>event/SignalEvent: remove method Set()<commit_after>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef SIGNAL_EVENT_HXX
#define SIGNAL_EVENT_HXX

#include "Event.hxx"

class SignalEvent {
    Event event;

public:
    SignalEvent() = default;

    SignalEvent(int sig, event_callback_fn callback, void *ctx) {
        event.SetSignal(sig, callback, ctx);
    }

    void Add(const struct timeval *timeout=nullptr) {
        event.Add(timeout);
    }

    void Delete() {
        event.Delete();
    }
};

#endif
<|endoftext|>


<commit_before>/*
 * Convert a stream into a stream of FCGI_STDIN packets.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_fcgi.hxx"
#include "FacadeIstream.hxx"
#include "fcgi_protocol.h"
#include "util/Cast.hxx"
#include "util/ByteOrder.hxx"

#include <assert.h>
#include <string.h>

class FcgiIstream : public FacadeIstream {
    size_t missing_from_current_record = 0;

    struct fcgi_record_header header;
    size_t header_sent = sizeof(header);

public:
    FcgiIstream(struct pool &pool, struct istream &_input,
                uint16_t request_id)
        :FacadeIstream(pool, _input,
                       MakeIstreamHandler<FcgiIstream>::handler, this) {
        header = (struct fcgi_record_header){
            .version = FCGI_VERSION_1,
            .type = FCGI_STDIN,
            .request_id = request_id,
            .padding_length = 0,
            .reserved = 0,
        };
    }

    bool WriteHeader();
    void StartRecord(size_t length);
    size_t Feed(const char *data, size_t length);

    /* virtual methods from class Istream */

    off_t GetAvailable(bool partial) override {
        return partial
            ? sizeof(header) - header_sent + input.GetAvailable(partial)
            : -1;
    }

    off_t Skip(gcc_unused off_t length) override {
        return -1;
    }

    void Read() override;

    int AsFd() override {
        return -1;
    }

    void Close() override;

    /* handler */

    size_t OnData(const void *data, size_t length) {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        return Feed((const char *)data, length);
    }

    ssize_t OnDirect(gcc_unused enum istream_direct type, gcc_unused int fd,
                     gcc_unused size_t max_length) {
        gcc_unreachable();
    }

    void OnEof();

    void OnError(GError *error) {
        ClearInput();
        DestroyError(error);
    }
};

bool
FcgiIstream::WriteHeader()
{
    assert(header_sent <= sizeof(header));

    size_t length = sizeof(header) - header_sent;
    if (length == 0)
        return true;

    const char *data = (char *)&header + header_sent;
    size_t nbytes = InvokeData(data, length);
    if (nbytes > 0)
        header_sent += nbytes;

    return nbytes == length;
}

void
FcgiIstream::StartRecord(size_t length)
{
    assert(missing_from_current_record == 0);
    assert(header_sent == sizeof(header));

    if (length > 0xffff)
        /* uint16_t's limit */
        length = 0xffff;

    header.content_length = ToBE16(length);
    header_sent = 0;
    missing_from_current_record = length;
}

size_t
FcgiIstream::Feed(const char *data, size_t length)
{
    assert(HasInput());

    size_t total = 0;
    while (true) {
        if (!WriteHeader())
            return HasInput() ? total : 0;

        if (missing_from_current_record > 0) {
            /* send the record header */
            size_t rest = length - total;
            if (rest > missing_from_current_record)
                rest = missing_from_current_record;

            size_t nbytes = InvokeData(data + total, rest);
            if (nbytes == 0)
                return HasInput() ? total : 0;

            total += nbytes;
            missing_from_current_record -= nbytes;

            if (missing_from_current_record > 0)
                /* not enough data or handler is blocking - return for
                   now */
                return total;
        }

        size_t rest = length - total;
        if (rest == 0)
            return total;

        StartRecord(rest);
    }
}

/*
 * istream handler
 *
 */

void
FcgiIstream::OnEof()
{
    assert(HasInput());
    assert(missing_from_current_record == 0);
    assert(header_sent == sizeof(header));

    ClearInput();

    /* write EOF record (length 0) */

    StartRecord(0);

    /* flush the buffer */

    if (WriteHeader())
        DestroyEof();
}

/*
 * istream implementation
 *
 */

void
FcgiIstream::Read()
{
    if (!WriteHeader())
        return;

    if (!HasInput()) {
        DestroyEof();
        return;
    }

    if (missing_from_current_record == 0) {
        off_t available = input.GetAvailable(true);
        if (available > 0) {
            StartRecord(available);
            if (!WriteHeader())
                return;
        }
    }

    input.Read();
}

void
FcgiIstream::Close()
{
    if (HasInput())
        input.ClearAndClose();

    Destroy();
}

/*
 * constructor
 *
 */

struct istream *
istream_fcgi_new(struct pool *pool, struct istream *input, uint16_t request_id)
{
    assert(input != nullptr);
    assert(!istream_has_handler(input));

    return NewIstream<FcgiIstream>(*pool, *input, request_id);
}
<commit_msg>istream_fcgi: make the class "final"<commit_after>/*
 * Convert a stream into a stream of FCGI_STDIN packets.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_fcgi.hxx"
#include "FacadeIstream.hxx"
#include "fcgi_protocol.h"
#include "util/Cast.hxx"
#include "util/ByteOrder.hxx"

#include <assert.h>
#include <string.h>

class FcgiIstream final : public FacadeIstream {
    size_t missing_from_current_record = 0;

    struct fcgi_record_header header;
    size_t header_sent = sizeof(header);

public:
    FcgiIstream(struct pool &pool, struct istream &_input,
                uint16_t request_id)
        :FacadeIstream(pool, _input,
                       MakeIstreamHandler<FcgiIstream>::handler, this) {
        header = (struct fcgi_record_header){
            .version = FCGI_VERSION_1,
            .type = FCGI_STDIN,
            .request_id = request_id,
            .padding_length = 0,
            .reserved = 0,
        };
    }

    bool WriteHeader();
    void StartRecord(size_t length);
    size_t Feed(const char *data, size_t length);

    /* virtual methods from class Istream */

    off_t GetAvailable(bool partial) override {
        return partial
            ? sizeof(header) - header_sent + input.GetAvailable(partial)
            : -1;
    }

    off_t Skip(gcc_unused off_t length) override {
        return -1;
    }

    void Read() override;

    int AsFd() override {
        return -1;
    }

    void Close() override;

    /* handler */

    size_t OnData(const void *data, size_t length) {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        return Feed((const char *)data, length);
    }

    ssize_t OnDirect(gcc_unused enum istream_direct type, gcc_unused int fd,
                     gcc_unused size_t max_length) {
        gcc_unreachable();
    }

    void OnEof();

    void OnError(GError *error) {
        ClearInput();
        DestroyError(error);
    }
};

bool
FcgiIstream::WriteHeader()
{
    assert(header_sent <= sizeof(header));

    size_t length = sizeof(header) - header_sent;
    if (length == 0)
        return true;

    const char *data = (char *)&header + header_sent;
    size_t nbytes = InvokeData(data, length);
    if (nbytes > 0)
        header_sent += nbytes;

    return nbytes == length;
}

void
FcgiIstream::StartRecord(size_t length)
{
    assert(missing_from_current_record == 0);
    assert(header_sent == sizeof(header));

    if (length > 0xffff)
        /* uint16_t's limit */
        length = 0xffff;

    header.content_length = ToBE16(length);
    header_sent = 0;
    missing_from_current_record = length;
}

size_t
FcgiIstream::Feed(const char *data, size_t length)
{
    assert(HasInput());

    size_t total = 0;
    while (true) {
        if (!WriteHeader())
            return HasInput() ? total : 0;

        if (missing_from_current_record > 0) {
            /* send the record header */
            size_t rest = length - total;
            if (rest > missing_from_current_record)
                rest = missing_from_current_record;

            size_t nbytes = InvokeData(data + total, rest);
            if (nbytes == 0)
                return HasInput() ? total : 0;

            total += nbytes;
            missing_from_current_record -= nbytes;

            if (missing_from_current_record > 0)
                /* not enough data or handler is blocking - return for
                   now */
                return total;
        }

        size_t rest = length - total;
        if (rest == 0)
            return total;

        StartRecord(rest);
    }
}

/*
 * istream handler
 *
 */

void
FcgiIstream::OnEof()
{
    assert(HasInput());
    assert(missing_from_current_record == 0);
    assert(header_sent == sizeof(header));

    ClearInput();

    /* write EOF record (length 0) */

    StartRecord(0);

    /* flush the buffer */

    if (WriteHeader())
        DestroyEof();
}

/*
 * istream implementation
 *
 */

void
FcgiIstream::Read()
{
    if (!WriteHeader())
        return;

    if (!HasInput()) {
        DestroyEof();
        return;
    }

    if (missing_from_current_record == 0) {
        off_t available = input.GetAvailable(true);
        if (available > 0) {
            StartRecord(available);
            if (!WriteHeader())
                return;
        }
    }

    input.Read();
}

void
FcgiIstream::Close()
{
    if (HasInput())
        input.ClearAndClose();

    Destroy();
}

/*
 * constructor
 *
 */

struct istream *
istream_fcgi_new(struct pool *pool, struct istream *input, uint16_t request_id)
{
    assert(input != nullptr);
    assert(!istream_has_handler(input));

    return NewIstream<FcgiIstream>(*pool, *input, request_id);
}
<|endoftext|>


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

#include "webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h"

#include <assert.h>

#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/desktop_frame_win.h"
#include "webrtc/modules/desktop_capture/desktop_region.h"
#include "webrtc/modules/desktop_capture/differ.h"
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
#include "webrtc/modules/desktop_capture/win/cursor.h"
#include "webrtc/modules/desktop_capture/win/desktop.h"
#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/tick_util.h"

namespace webrtc {

namespace {

// Constants from dwmapi.h.
const UINT DWM_EC_DISABLECOMPOSITION = 0;
const UINT DWM_EC_ENABLECOMPOSITION = 1;

const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";

}  // namespace

ScreenCapturerWinGdi::ScreenCapturerWinGdi(const DesktopCaptureOptions& options)
    : callback_(NULL),
      current_screen_id_(kFullDesktopScreenId),
      desktop_dc_(NULL),
      memory_dc_(NULL),
      dwmapi_library_(NULL),
      composition_func_(NULL),
      set_thread_execution_state_failed_(false) {
  if (options.disable_effects()) {
    // Load dwmapi.dll dynamically since it is not available on XP.
    if (!dwmapi_library_)
      dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);

    if (dwmapi_library_) {
      composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
          GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
    }
  }
}

ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
  if (desktop_dc_)
    ReleaseDC(NULL, desktop_dc_);
  if (memory_dc_)
    DeleteDC(memory_dc_);

  // Restore Aero.
  if (composition_func_)
    (*composition_func_)(DWM_EC_ENABLECOMPOSITION);

  if (dwmapi_library_)
    FreeLibrary(dwmapi_library_);
}

void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) {
  TickTime capture_start_time = TickTime::Now();

  queue_.MoveToNextFrame();

  // Request that the system not power-down the system, or the display hardware.
  if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
    if (!set_thread_execution_state_failed_) {
      set_thread_execution_state_failed_ = true;
      LOG_F(LS_WARNING) << "Failed to make system & display power assertion: "
                        << GetLastError();
    }
  }

  // Make sure the GDI capture resources are up-to-date.
  PrepareCaptureResources();

  if (!CaptureImage()) {
    callback_->OnCaptureCompleted(NULL);
    return;
  }

  const DesktopFrame* current_frame = queue_.current_frame();
  const DesktopFrame* last_frame = queue_.previous_frame();
  if (last_frame && last_frame->size().equals(current_frame->size())) {
    // Make sure the differencer is set up correctly for these previous and
    // current screens.
    if (!differ_.get() ||
        (differ_->width() != current_frame->size().width()) ||
        (differ_->height() != current_frame->size().height()) ||
        (differ_->bytes_per_row() != current_frame->stride())) {
      differ_.reset(new Differ(current_frame->size().width(),
                               current_frame->size().height(),
                               DesktopFrame::kBytesPerPixel,
                               current_frame->stride()));
    }

    // Calculate difference between the two last captured frames.
    DesktopRegion region;
    differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(),
                             &region);
    helper_.InvalidateRegion(region);
  } else {
    // No previous frame is available, or the screen is resized. Invalidate the
    // whole screen.
    helper_.InvalidateScreen(current_frame->size());
  }

  helper_.set_size_most_recent(current_frame->size());

  // Emit the current frame.
  DesktopFrame* frame = queue_.current_frame()->Share();
  frame->set_dpi(DesktopVector(
      GetDeviceCaps(desktop_dc_, LOGPIXELSX),
      GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
  frame->mutable_updated_region()->Clear();
  helper_.TakeInvalidRegion(frame->mutable_updated_region());
  frame->set_capture_time_ms(
      (TickTime::Now() - capture_start_time).Milliseconds());
  callback_->OnCaptureCompleted(frame);
}

bool ScreenCapturerWinGdi::GetScreenList(ScreenList* screens) {
  return webrtc::GetScreenList(screens);
}

bool ScreenCapturerWinGdi::SelectScreen(ScreenId id) {
  bool valid = IsScreenValid(id, &current_device_key_);
  if (valid)
    current_screen_id_ = id;
  return valid;
}

void ScreenCapturerWinGdi::Start(Callback* callback) {
  assert(!callback_);
  assert(callback);

  callback_ = callback;

  // Vote to disable Aero composited desktop effects while capturing. Windows
  // will restore Aero automatically if the process exits. This has no effect
  // under Windows 8 or higher.  See crbug.com/124018.
  if (composition_func_)
    (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
}

void ScreenCapturerWinGdi::PrepareCaptureResources() {
  // Switch to the desktop receiving user input if different from the current
  // one.
  rtc::scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
  if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
    // Release GDI resources otherwise SetThreadDesktop will fail.
    if (desktop_dc_) {
      ReleaseDC(NULL, desktop_dc_);
      desktop_dc_ = NULL;
    }

    if (memory_dc_) {
      DeleteDC(memory_dc_);
      memory_dc_ = NULL;
    }

    // If SetThreadDesktop() fails, the thread is still assigned a desktop.
    // So we can continue capture screen bits, just from the wrong desktop.
    desktop_.SetThreadDesktop(input_desktop.release());

    // Re-assert our vote to disable Aero.
    // See crbug.com/124018 and crbug.com/129906.
    if (composition_func_ != NULL) {
      (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
    }
  }

  // If the display bounds have changed then recreate GDI resources.
  // TODO(wez): Also check for pixel format changes.
  DesktopRect screen_rect(DesktopRect::MakeXYWH(
      GetSystemMetrics(SM_XVIRTUALSCREEN),
      GetSystemMetrics(SM_YVIRTUALSCREEN),
      GetSystemMetrics(SM_CXVIRTUALSCREEN),
      GetSystemMetrics(SM_CYVIRTUALSCREEN)));
  if (!screen_rect.equals(desktop_dc_rect_)) {
    if (desktop_dc_) {
      ReleaseDC(NULL, desktop_dc_);
      desktop_dc_ = NULL;
    }
    if (memory_dc_) {
      DeleteDC(memory_dc_);
      memory_dc_ = NULL;
    }
    desktop_dc_rect_ = DesktopRect();
  }

  if (desktop_dc_ == NULL) {
    assert(memory_dc_ == NULL);

    // Create GDI device contexts to capture from the desktop into memory.
    desktop_dc_ = GetDC(NULL);
    if (!desktop_dc_)
      abort();
    memory_dc_ = CreateCompatibleDC(desktop_dc_);
    if (!memory_dc_)
      abort();

    desktop_dc_rect_ = screen_rect;

    // Make sure the frame buffers will be reallocated.
    queue_.Reset();

    helper_.ClearInvalidRegion();
  }
}

bool ScreenCapturerWinGdi::CaptureImage() {
  DesktopRect screen_rect =
      GetScreenRect(current_screen_id_, current_device_key_);
  if (screen_rect.is_empty())
    return false;

  DesktopSize size = screen_rect.size();
  // If the current buffer is from an older generation then allocate a new one.
  // Note that we can't reallocate other buffers at this point, since the caller
  // may still be reading from them.
  if (!queue_.current_frame() ||
      !queue_.current_frame()->size().equals(screen_rect.size())) {
    assert(desktop_dc_ != NULL);
    assert(memory_dc_ != NULL);

    size_t buffer_size = size.width() * size.height() *
        DesktopFrame::kBytesPerPixel;
    SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size);

    rtc::scoped_ptr<DesktopFrame> buffer;
    buffer.reset(
        DesktopFrameWin::Create(size, shared_memory, desktop_dc_));
    queue_.ReplaceCurrentFrame(buffer.release());
  }

  // Select the target bitmap into the memory dc and copy the rect from desktop
  // to memory.
  DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
      queue_.current_frame()->GetUnderlyingFrame());
  HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
  if (previous_object != NULL) {
    BitBlt(memory_dc_,
           0, 0, screen_rect.width(), screen_rect.height(),
           desktop_dc_,
           screen_rect.left(), screen_rect.top(),
           SRCCOPY | CAPTUREBLT);

    // Select back the previously selected object to that the device contect
    // could be destroyed independently of the bitmap if needed.
    SelectObject(memory_dc_, previous_object);
  }
  return true;
}

}  // namespace webrtc
<commit_msg>Fix ScreenCapturerWinGdi to handle DesktopFrameWin::Create() errors.<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h"

#include <assert.h>

#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/desktop_frame_win.h"
#include "webrtc/modules/desktop_capture/desktop_region.h"
#include "webrtc/modules/desktop_capture/differ.h"
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
#include "webrtc/modules/desktop_capture/win/cursor.h"
#include "webrtc/modules/desktop_capture/win/desktop.h"
#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/tick_util.h"

namespace webrtc {

namespace {

// Constants from dwmapi.h.
const UINT DWM_EC_DISABLECOMPOSITION = 0;
const UINT DWM_EC_ENABLECOMPOSITION = 1;

const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";

}  // namespace

ScreenCapturerWinGdi::ScreenCapturerWinGdi(const DesktopCaptureOptions& options)
    : callback_(NULL),
      current_screen_id_(kFullDesktopScreenId),
      desktop_dc_(NULL),
      memory_dc_(NULL),
      dwmapi_library_(NULL),
      composition_func_(NULL),
      set_thread_execution_state_failed_(false) {
  if (options.disable_effects()) {
    // Load dwmapi.dll dynamically since it is not available on XP.
    if (!dwmapi_library_)
      dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);

    if (dwmapi_library_) {
      composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
          GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
    }
  }
}

ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
  if (desktop_dc_)
    ReleaseDC(NULL, desktop_dc_);
  if (memory_dc_)
    DeleteDC(memory_dc_);

  // Restore Aero.
  if (composition_func_)
    (*composition_func_)(DWM_EC_ENABLECOMPOSITION);

  if (dwmapi_library_)
    FreeLibrary(dwmapi_library_);
}

void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) {
  TickTime capture_start_time = TickTime::Now();

  queue_.MoveToNextFrame();

  // Request that the system not power-down the system, or the display hardware.
  if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
    if (!set_thread_execution_state_failed_) {
      set_thread_execution_state_failed_ = true;
      LOG_F(LS_WARNING) << "Failed to make system & display power assertion: "
                        << GetLastError();
    }
  }

  // Make sure the GDI capture resources are up-to-date.
  PrepareCaptureResources();

  if (!CaptureImage()) {
    callback_->OnCaptureCompleted(NULL);
    return;
  }

  const DesktopFrame* current_frame = queue_.current_frame();
  const DesktopFrame* last_frame = queue_.previous_frame();
  if (last_frame && last_frame->size().equals(current_frame->size())) {
    // Make sure the differencer is set up correctly for these previous and
    // current screens.
    if (!differ_.get() ||
        (differ_->width() != current_frame->size().width()) ||
        (differ_->height() != current_frame->size().height()) ||
        (differ_->bytes_per_row() != current_frame->stride())) {
      differ_.reset(new Differ(current_frame->size().width(),
                               current_frame->size().height(),
                               DesktopFrame::kBytesPerPixel,
                               current_frame->stride()));
    }

    // Calculate difference between the two last captured frames.
    DesktopRegion region;
    differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(),
                             &region);
    helper_.InvalidateRegion(region);
  } else {
    // No previous frame is available, or the screen is resized. Invalidate the
    // whole screen.
    helper_.InvalidateScreen(current_frame->size());
  }

  helper_.set_size_most_recent(current_frame->size());

  // Emit the current frame.
  DesktopFrame* frame = queue_.current_frame()->Share();
  frame->set_dpi(DesktopVector(
      GetDeviceCaps(desktop_dc_, LOGPIXELSX),
      GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
  frame->mutable_updated_region()->Clear();
  helper_.TakeInvalidRegion(frame->mutable_updated_region());
  frame->set_capture_time_ms(
      (TickTime::Now() - capture_start_time).Milliseconds());
  callback_->OnCaptureCompleted(frame);
}

bool ScreenCapturerWinGdi::GetScreenList(ScreenList* screens) {
  return webrtc::GetScreenList(screens);
}

bool ScreenCapturerWinGdi::SelectScreen(ScreenId id) {
  bool valid = IsScreenValid(id, &current_device_key_);
  if (valid)
    current_screen_id_ = id;
  return valid;
}

void ScreenCapturerWinGdi::Start(Callback* callback) {
  assert(!callback_);
  assert(callback);

  callback_ = callback;

  // Vote to disable Aero composited desktop effects while capturing. Windows
  // will restore Aero automatically if the process exits. This has no effect
  // under Windows 8 or higher.  See crbug.com/124018.
  if (composition_func_)
    (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
}

void ScreenCapturerWinGdi::PrepareCaptureResources() {
  // Switch to the desktop receiving user input if different from the current
  // one.
  rtc::scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
  if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
    // Release GDI resources otherwise SetThreadDesktop will fail.
    if (desktop_dc_) {
      ReleaseDC(NULL, desktop_dc_);
      desktop_dc_ = NULL;
    }

    if (memory_dc_) {
      DeleteDC(memory_dc_);
      memory_dc_ = NULL;
    }

    // If SetThreadDesktop() fails, the thread is still assigned a desktop.
    // So we can continue capture screen bits, just from the wrong desktop.
    desktop_.SetThreadDesktop(input_desktop.release());

    // Re-assert our vote to disable Aero.
    // See crbug.com/124018 and crbug.com/129906.
    if (composition_func_ != NULL) {
      (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
    }
  }

  // If the display bounds have changed then recreate GDI resources.
  // TODO(wez): Also check for pixel format changes.
  DesktopRect screen_rect(DesktopRect::MakeXYWH(
      GetSystemMetrics(SM_XVIRTUALSCREEN),
      GetSystemMetrics(SM_YVIRTUALSCREEN),
      GetSystemMetrics(SM_CXVIRTUALSCREEN),
      GetSystemMetrics(SM_CYVIRTUALSCREEN)));
  if (!screen_rect.equals(desktop_dc_rect_)) {
    if (desktop_dc_) {
      ReleaseDC(NULL, desktop_dc_);
      desktop_dc_ = NULL;
    }
    if (memory_dc_) {
      DeleteDC(memory_dc_);
      memory_dc_ = NULL;
    }
    desktop_dc_rect_ = DesktopRect();
  }

  if (desktop_dc_ == NULL) {
    assert(memory_dc_ == NULL);

    // Create GDI device contexts to capture from the desktop into memory.
    desktop_dc_ = GetDC(NULL);
    if (!desktop_dc_)
      abort();
    memory_dc_ = CreateCompatibleDC(desktop_dc_);
    if (!memory_dc_)
      abort();

    desktop_dc_rect_ = screen_rect;

    // Make sure the frame buffers will be reallocated.
    queue_.Reset();

    helper_.ClearInvalidRegion();
  }
}

bool ScreenCapturerWinGdi::CaptureImage() {
  DesktopRect screen_rect =
      GetScreenRect(current_screen_id_, current_device_key_);
  if (screen_rect.is_empty())
    return false;

  DesktopSize size = screen_rect.size();
  // If the current buffer is from an older generation then allocate a new one.
  // Note that we can't reallocate other buffers at this point, since the caller
  // may still be reading from them.
  if (!queue_.current_frame() ||
      !queue_.current_frame()->size().equals(screen_rect.size())) {
    assert(desktop_dc_ != NULL);
    assert(memory_dc_ != NULL);

    size_t buffer_size = size.width() * size.height() *
        DesktopFrame::kBytesPerPixel;
    SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size);

    rtc::scoped_ptr<DesktopFrame> buffer(
        DesktopFrameWin::Create(size, shared_memory, desktop_dc_));
    if (!buffer.get())
      return false;
    queue_.ReplaceCurrentFrame(buffer.release());
  }

  // Select the target bitmap into the memory dc and copy the rect from desktop
  // to memory.
  DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
      queue_.current_frame()->GetUnderlyingFrame());
  HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
  if (previous_object != NULL) {
    BitBlt(memory_dc_,
           0, 0, screen_rect.width(), screen_rect.height(),
           desktop_dc_,
           screen_rect.left(), screen_rect.top(),
           SRCCOPY | CAPTUREBLT);

    // Select back the previously selected object to that the device contect
    // could be destroyed independently of the bitmap if needed.
    SelectObject(memory_dc_, previous_object);
  }
  return true;
}

}  // namespace webrtc
<|endoftext|>


<commit_before>/*!
 * Copyright 2019 by Contributors
 */
#include <utility>
#include <limits>
#include "xgboost/json.h"
#include "gblinear_model.h"

namespace xgboost {
namespace gbm {

void GBLinearModel::SaveModel(Json* p_out) const {
  using WeightType = std::remove_reference<decltype(std::declval<decltype(weight)>().back())>::type;
  using JsonFloat = Number::Float;
  static_assert(std::is_same<WeightType, JsonFloat>::value,
                "Weight type should be of the same type with JSON float");
  auto& out = *p_out;

  size_t const n_weights = weight.size();
  std::vector<Json> j_weights(n_weights);
  for (size_t i = 0; i < n_weights; ++i) {
    j_weights[i] = weight[i];
  }
  out["weights"] = std::move(j_weights);
}

void GBLinearModel::LoadModel(Json const& in) {
  auto const& j_weights = get<Array const>(in["weights"]);
  auto n_weights = j_weights.size();
  weight.resize(n_weights);
  for (size_t i = 0; i < n_weights; ++i) {
    weight[i] = get<Number const>(j_weights[i]);
  }
}

DMLC_REGISTER_PARAMETER(DeprecatedGBLinearModelParam);
}  // namespace gbm
}  // namespace xgboost
<commit_msg>Remove type check for solaris. (#6610)<commit_after>/*!
 * Copyright 2019-2021 by Contributors
 */
#include <utility>
#include <limits>
#include "xgboost/json.h"
#include "gblinear_model.h"

namespace xgboost {
namespace gbm {

void GBLinearModel::SaveModel(Json* p_out) const {
  auto& out = *p_out;

  size_t const n_weights = weight.size();
  std::vector<Json> j_weights(n_weights);
  for (size_t i = 0; i < n_weights; ++i) {
    j_weights[i] = weight[i];
  }
  out["weights"] = std::move(j_weights);
}

void GBLinearModel::LoadModel(Json const& in) {
  auto const& j_weights = get<Array const>(in["weights"]);
  auto n_weights = j_weights.size();
  weight.resize(n_weights);
  for (size_t i = 0; i < n_weights; ++i) {
    weight[i] = get<Number const>(j_weights[i]);
  }
}

DMLC_REGISTER_PARAMETER(DeprecatedGBLinearModelParam);
}  // namespace gbm
}  // namespace xgboost
<|endoftext|>