Skip to content

Commit

Permalink
[example] C++ Debugging (grpc#35222)
Browse files Browse the repository at this point in the history
  • Loading branch information
drfloob authored Dec 6, 2023
1 parent 295b665 commit 8c37846
Show file tree
Hide file tree
Showing 4 changed files with 352 additions and 0 deletions.
42 changes: 42 additions & 0 deletions examples/cpp/debugging/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2023 the gRPC authors.
#
# 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.

licenses(["notice"])

cc_binary(
name = "crashing_greeter_client",
srcs = ["crashing_greeter_client.cc"],
defines = ["BAZEL_BUILD"],
deps = [
"//:grpc++",
"//examples/protos:helloworld_cc_grpc",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
],
)

cc_binary(
name = "greeter_callback_server_admin",
srcs = ["greeter_callback_server_admin.cc"],
defines = ["BAZEL_BUILD"],
deps = [
"//:grpc++",
"//:grpc++_reflection",
"//:grpcpp_admin",
"//examples/protos:helloworld_cc_grpc",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@com_google_absl//absl/strings:str_format",
],
)
132 changes: 132 additions & 0 deletions examples/cpp/debugging/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# gRPC C++ Debugging Example

This example demonstrates a handful of ways you can debug your gRPC C++ applications.

## Enabling Trace Logs

gRPC allows you to configure more detailed log output for various aspects of gRPC behavior. The tracing log generation might have a large overhead and result in significantly larger log file sizes, especially when you try to trace transport or timer_check. But it is a powerful tool in your debugging toolkit.

### The Most Verbose Logging

Specify environment variables, then run your application:

```
GRPC_VERBOSITY=debug
GRPC_TRACE=all
```

For more granularity, please see
[environment_variables](https://github.com/grpc/grpc/blob/master/doc/environment_variables.md).

### Debug Transport Protocol

```
GRPC_VERBOSITY=debug
GRPC_TRACE=tcp,http,secure_endpoint,transport_security
```

### Debug Connection Behavior

```
GRPC_VERBOSITY=debug
GRPC_TRACE=call_error,connectivity_state,pick_first,round_robin,glb
```

## GDB and other debuggers

`gdb` (and the like) are tools that lets you inspect your application while it is running, view stack traces on exceptions, pause and step through code at specified points or under certain conditions, etc. See https://www.sourceware.org/gdb/

### Inspecting errors

```
bazel build --config=dbg examples/cpp/debugging:crashing_greeter_client
gdb -ex run \
--args ./bazel-bin/examples/cpp/debugging/crashing_greeter_client \
--crash_on_errors=true \
--target=localhork:50051
```

Once the exception is thrown, you can use `bt` to see the stack trace and examine the crash, `info threads` to get the set of threads, etc. See the [GDB documentation](https://sourceware.org/gdb/current/onlinedocs/gdb.html/) for a more complete list of available features and commands.

### Breaking inside a function

After building the application above, this will break inside gRPC generated stub code:

```
gdb -ex 'b helloworld::Greeter::Stub::SayHello' \
-ex run \
--args ./bazel-bin/examples/cpp/debugging/crashing_greeter_client \
--crash_on_errors=true \
--target=localhork:50051
```

## gRPC Admin Interface: Live Channel Tracing

The [gRPC Admin Service](https://github.com/grpc/proposal/blob/master/A38-admin-interface-api.md)
provides a convenient API in each gRPC language to improve the usability of
creating a gRPC server with admin services to expose states in the gRPC library.
This includes channelz, which is a channel tracing feature; it tracks statistics
like how many messages have been sent, how many of them failed, what are the
connected sockets. See the [Channelz design doc](https://github.com/grpc/proposal/blob/master/A14-channelz.md).

### Integrating the gRPC Admin Service Into Your Server

As seen in the `greeter_callback_admin_server` target, you canenable admin services by using the `AddAdminServices` method.

```
grpc::ServerBuilder builder;
grpc::AddAdminServices(&builder);
builder.AddListeningPort(":50051", grpc::ServerCredentials(...));
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
```

### Using grpcdebug

grpcdebug is a tool created to access the metrics from channelz and health services.

#### Installing the grpcdebug tool

The source code is located in a github project
[grpc-ecosystem/grpcdebug](https://github.com/grpc-ecosystem/grpcdebug). You
can either download [the latest built version]
(https://github.com/grpc-ecosystem/grpcdebug/releases/latest) (recommended) or
follow the README.md to build it yourself.

#### Running the grpcdebug tool
##### Usage
`grpcdebug <target address> [flags] channelz <command> [argument]`


| Command | Argument | Description |
| :--------- | :------------------: | :------------------------------------------------ |
| channel | \<channel id or URL> | Display channel states in a human readable way. |
| channels | | Lists client channels for the target application. |
| server | \<server ID> | Displays server state in a human readable way. |
| servers | | Lists servers in a human readable way. |
| socket | \<socket ID> | Displays socket states in a human readable way. |
| subchannel | \<id> | Display subchannel states in human readable way. |

Generally, you will start with either `servers` or `channels` and then work down
to the details

##### Getting overall server info

To begin with, build and run the server binary in the background

```
bazel build --config=dbg examples/cpp/debugging:all
./bazel-bin/examples/cpp/debugging/greeter_callback_server_admin&
```

You can then inspect the server
```bash
grpcdebug localhost:50051 channelz servers
```

This will show you the server ids with their activity
```text
Server ID Listen Addresses Calls(Started/Succeeded/Failed) Last Call Started
1 [[::]:50051] 38/34/3 now
```

For more information about `grpcdebug` features, please see [the grpcdebug documentation](https://github.com/grpc-ecosystem/grpcdebug)
92 changes: 92 additions & 0 deletions examples/cpp/debugging/crashing_greeter_client.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2023 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <memory>
#include <string>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"

#include <grpcpp/grpcpp.h>

#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif

ABSL_FLAG(bool, crash_on_errors, false,
"Crash the application on client errors");
ABSL_FLAG(std::string, target, "localhost:50051", "Server address");

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;

class GreeterClient {
public:
GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}

// Assembles the client's payload, sends it and presents the response back
// from the server.
std::string SayHello(const std::string& user) {
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);

// Container for the data we expect from the server.
HelloReply reply;

// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;

// The actual RPC.
Status status = stub_->SayHello(&context, request, &reply);

// Act upon the status of the actual RPC.
if (absl::GetFlag(FLAGS_crash_on_errors)) {
assert(status.ok());
}
if (status.ok()) {
return reply.message();
} else {
return "RPC failed";
}
}

private:
std::unique_ptr<Greeter::Stub> stub_;
};

int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint specified by
// the argument "--target=" which is the only expected argument.
// We indicate that the channel isn't authenticated (use of
// InsecureChannelCredentials()).
GreeterClient greeter(grpc::CreateChannel(
absl::GetFlag(FLAGS_target), grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;

return 0;
}
86 changes: 86 additions & 0 deletions examples/cpp/debugging/greeter_callback_server_admin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2023 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <memory>
#include <string>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/str_format.h"

#include <grpcpp/ext/admin_services.h>
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>

#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif

ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");

using grpc::CallbackServerContext;
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerUnaryReactor;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;

// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::CallbackService {
ServerUnaryReactor* SayHello(CallbackServerContext* context,
const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
reply->set_message(prefix + request->name());

ServerUnaryReactor* reactor = context->DefaultReactor();
reactor->Finish(Status::OK);
return reactor;
}
};

void RunServer(uint16_t port) {
std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
GreeterServiceImpl service;

grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
ServerBuilder builder;
// Enable gRPC Admin Services
grpc::AddAdminServices(&builder);
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *synchronous* service.
builder.RegisterService(&service);
// Finally assemble the server.
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;

// Wait for the server to shutdown. Note that some other thread must be
// responsible for shutting down the server for this call to ever return.
server->Wait();
}

int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
RunServer(absl::GetFlag(FLAGS_port));
return 0;
}

0 comments on commit 8c37846

Please sign in to comment.