Skip to content

Commit

Permalink
Merge pull request #53 from JDevlieghere/cherrypick/latest-reproducers
Browse files Browse the repository at this point in the history
Cherrypick/latest reproducers
  • Loading branch information
JDevlieghere authored Oct 29, 2019
2 parents c6ee456 + 0cfa969 commit bec7fde
Show file tree
Hide file tree
Showing 29 changed files with 766 additions and 361 deletions.
117 changes: 117 additions & 0 deletions lldb/include/lldb/Utility/GDBRemote.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//===-- GDBRemote.h ----------------------------------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_GDBRemote_h_
#define liblldb_GDBRemote_h_

#include "lldb/Utility/StreamString.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-public.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"

#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>

namespace lldb_private {

class StreamGDBRemote : public StreamString {
public:
StreamGDBRemote();

StreamGDBRemote(uint32_t flags, uint32_t addr_size,
lldb::ByteOrder byte_order);

~StreamGDBRemote() override;

/// Output a block of data to the stream performing GDB-remote escaping.
///
/// \param[in] s
/// A block of data.
///
/// \param[in] src_len
/// The amount of data to write.
///
/// \return
/// Number of bytes written.
// TODO: Convert this function to take ArrayRef<uint8_t>
int PutEscapedBytes(const void *s, size_t src_len);
};

/// GDB remote packet as used by the reproducer and the GDB remote
/// communication history. Packets can be serialized to file.
struct GDBRemotePacket {

friend llvm::yaml::MappingTraits<GDBRemotePacket>;

enum Type { ePacketTypeInvalid = 0, ePacketTypeSend, ePacketTypeRecv };

GDBRemotePacket()
: packet(), type(ePacketTypeInvalid), bytes_transmitted(0), packet_idx(0),
tid(LLDB_INVALID_THREAD_ID) {}

void Clear() {
packet.data.clear();
type = ePacketTypeInvalid;
bytes_transmitted = 0;
packet_idx = 0;
tid = LLDB_INVALID_THREAD_ID;
}

struct BinaryData {
std::string data;
};

void Serialize(llvm::raw_ostream &strm) const;
void Dump(Stream &strm) const;

BinaryData packet;
Type type;
uint32_t bytes_transmitted;
uint32_t packet_idx;
lldb::tid_t tid;

private:
llvm::StringRef GetTypeStr() const;
};

} // namespace lldb_private

LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(lldb_private::GDBRemotePacket)
LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(std::vector<lldb_private::GDBRemotePacket>)

namespace llvm {
namespace yaml {

template <>
struct ScalarEnumerationTraits<lldb_private::GDBRemotePacket::Type> {
static void enumeration(IO &io, lldb_private::GDBRemotePacket::Type &value);
};

template <> struct ScalarTraits<lldb_private::GDBRemotePacket::BinaryData> {
static void output(const lldb_private::GDBRemotePacket::BinaryData &, void *,
raw_ostream &);

static StringRef input(StringRef, void *,
lldb_private::GDBRemotePacket::BinaryData &);

static QuotingType mustQuote(StringRef S) { return QuotingType::None; }
};

template <> struct MappingTraits<lldb_private::GDBRemotePacket> {
static void mapping(IO &io, lldb_private::GDBRemotePacket &Packet);

static StringRef validate(IO &io, lldb_private::GDBRemotePacket &);
};

} // namespace yaml
} // namespace llvm

#endif // liblldb_GDBRemote_h_
78 changes: 75 additions & 3 deletions lldb/include/lldb/Utility/Reproducer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "lldb/Utility/FileCollector.h"
#include "lldb/Utility/FileSpec.h"

#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/YAMLTraits.h"
Expand Down Expand Up @@ -130,6 +129,27 @@ class VersionProvider : public Provider<VersionProvider> {
static char ID;
};

/// Provider for the LLDB current working directroy.
///
/// When the reproducer is kept, it writes lldb's current working directory to
/// a file named cwd.txt in the reproducer root.
class WorkingDirectoryProvider : public Provider<WorkingDirectoryProvider> {
public:
WorkingDirectoryProvider(const FileSpec &directory) : Provider(directory) {
llvm::SmallString<128> cwd;
if (std::error_code EC = llvm::sys::fs::current_path(cwd))
return;
m_cwd = cwd.str();
}
struct Info {
static const char *name;
static const char *file;
};
void Keep() override;
std::string m_cwd;
static char ID;
};

class DataRecorder {
public:
DataRecorder(const FileSpec &filename, std::error_code &ec)
Expand Down Expand Up @@ -181,12 +201,39 @@ class CommandProvider : public Provider<CommandProvider> {
std::vector<std::unique_ptr<DataRecorder>> m_data_recorders;
};

class ProcessGDBRemoteProvider
: public repro::Provider<ProcessGDBRemoteProvider> {
public:
struct Info {
static const char *name;
static const char *file;
};

ProcessGDBRemoteProvider(const FileSpec &directory) : Provider(directory) {}

llvm::raw_ostream *GetHistoryStream();

void SetCallback(std::function<void()> callback) {
m_callback = std::move(callback);
}

void Keep() override { m_callback(); }
void Discard() override { m_callback(); }

static char ID;

private:
std::function<void()> m_callback;
std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;
};

/// The generator is responsible for the logic needed to generate a
/// reproducer. For doing so it relies on providers, who serialize data that
/// is necessary for reproducing a failure.
class Generator final {

public:
Generator(const FileSpec &root);
Generator(FileSpec root);
~Generator();

/// Method to indicate we want to keep the reproducer. If reproducer
Expand Down Expand Up @@ -243,7 +290,7 @@ class Generator final {

class Loader final {
public:
Loader(const FileSpec &root);
Loader(FileSpec root);

template <typename T> FileSpec GetFile() {
if (!HasFile(T::file))
Expand All @@ -252,6 +299,15 @@ class Loader final {
return GetRoot().CopyByAppendingPathComponent(T::file);
}

template <typename T> llvm::Expected<std::string> LoadBuffer() {
FileSpec file = GetFile<typename T::Info>();
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
llvm::vfs::getRealFileSystem()->getBufferForFile(file.GetPath());
if (!buffer)
return llvm::errorCodeToError(buffer.getError());
return (*buffer)->getBuffer().str();
}

llvm::Error LoadIndex();

const FileSpec &GetRoot() const { return m_root; }
Expand Down Expand Up @@ -284,6 +340,9 @@ class Reproducer {

FileSpec GetReproducerPath() const;

bool IsCapturing() { return static_cast<bool>(m_generator); };
bool IsReplaying() { return static_cast<bool>(m_loader); };

protected:
llvm::Error SetCapture(llvm::Optional<FileSpec> root);
llvm::Error SetReplay(llvm::Optional<FileSpec> root);
Expand All @@ -297,6 +356,19 @@ class Reproducer {
mutable std::mutex m_mutex;
};

/// Helper class for replaying commands through the reproducer.
class CommandLoader {
public:
CommandLoader(std::vector<std::string> files) : m_files(files) {}

static std::unique_ptr<CommandLoader> Create(Loader *loader);
llvm::Optional<std::string> GetNextFile();

private:
std::vector<std::string> m_files;
unsigned m_index = 0;
};

} // namespace repro
} // namespace lldb_private

Expand Down
45 changes: 0 additions & 45 deletions lldb/include/lldb/Utility/StreamGDBRemote.h

This file was deleted.

1 change: 1 addition & 0 deletions lldb/lit/Reproducer/Inputs/FileCapture.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
run
reproducer status
reproducer dump -p files
reproducer generate
4 changes: 4 additions & 0 deletions lldb/lit/Reproducer/Inputs/WorkingDir.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
run
reproducer status
reproducer dump -p cwd
reproducer generate
25 changes: 25 additions & 0 deletions lldb/lit/Reproducer/TestDump.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This tests the reproducer dump functionality.

# Generate a reproducer.
# RUN: mkdir -p %t
# RUN: rm -rf %t.repro
# RUN: %clang %S/Inputs/simple.c -g -o %t/reproducer.out
# RUN: %lldb -x -b -s %S/Inputs/FileCapture.in -o 'reproducer dump -p files' --capture --capture-path %t.repro %t/reproducer.out

# RUN: %lldb -b -o 'reproducer dump -p files -f %t.repro' | FileCheck %s --check-prefix FILES
# FILES: 'reproducer.out'
# FILES: 'FileCapture.in'

# RUN: %lldb -b -o 'reproducer dump -p version -f %t.repro' | FileCheck %s --check-prefix VERSION
# VERSION: lldb version

# RUN: %lldb -b -o 'reproducer dump -p commands -f %t.repro' | FileCheck %s --check-prefix COMMANDS
# COMMANDS: command source
# COMMANDS: target create
# COMMANDS: command source

# RUN: %lldb -b -o 'reproducer dump -p gdb -f %t.repro' | FileCheck %s --check-prefix GDB
# GDB: send packet: $QStartNoAckMode#b0
# GDB: read packet: $OK#9a

# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix FILES
8 changes: 8 additions & 0 deletions lldb/lit/Reproducer/TestRelativePath.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This tests relative capture paths.

# RUN: mkdir -p %t
# RUN: cd %t
# RUN: rm -rf ./foo
# RUN: %clang %S/Inputs/simple.c -g -o %t/reproducer.out
# RUN: %lldb -x -b -s %S/Inputs/FileCapture.in -o 'reproducer dump -p files' --capture --capture-path ./foo %t/reproducer.out
# RUN: %lldb --replay ./foo
13 changes: 13 additions & 0 deletions lldb/lit/Reproducer/TestWorkingDir.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# This tests relative capture paths.

# RUN: echo "CHECK: %t" > %t.check

# RUN: rm -rf %t.repro
# RUN: mkdir -p %t.repro
# RUN: mkdir -p %t
# RUN: cd %t
# RUN: %clang %S/Inputs/simple.c -g -o %t/reproducer.out
# RUN: %lldb -x -b -s %S/Inputs/WorkingDir.in --capture --capture-path %t.repro %t/reproducer.out

# RUN: cat %t.repro/cwd.txt | FileCheck %t.check
# RUN: %lldb --replay %t.repro | FileCheck %t.check
Loading

0 comments on commit bec7fde

Please sign in to comment.