Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherrypick/latest reproducers #53

Merged
merged 12 commits into from
Oct 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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