Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b759124 authored by Andrew Walbran's avatar Andrew Walbran
Browse files

Add Rust wrapper for tombstoned client using cxx.

Bug: 226162295
Test: atest libtombstoned_client_rust_test
Change-Id: Ibe7c41e2381f0f369a76175d6f71fc60b71cc7d5
parent ca0c5c5b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2,6 +2,9 @@
  "presubmit": [
    {
      "name": "debuggerd_test"
    },
    {
      "name": "libtombstoned_client_rust_test"
    }
  ]
}
+58 −0
Original line number Diff line number Diff line
package {
    default_applicable_licenses: ["Android-Apache-2.0"],
}

cc_library_static {
    name: "libtombstoned_client_wrapper",
    srcs: [
        "wrapper.cpp",
    ],
    generated_sources: [
        "libtombstoned_client_rust_bridge_code"
    ],
    header_libs: [
        "libbase_headers",
        "libdebuggerd_common_headers",
    ],
    shared_libs: [
        "libtombstoned_client",
    ],
}

rust_defaults {
    name: "libtombstoned_client_rust_defaults",
    crate_name: "tombstoned_client",
    srcs: ["src/lib.rs"],
    edition: "2021",
    rustlibs: [
        "libcxx",
        "libthiserror",
    ],
    static_libs: [
        "libtombstoned_client_wrapper",
    ],
    shared_libs: [
        "libtombstoned_client",
    ],
}

rust_library {
    name: "libtombstoned_client_rust",
    defaults: ["libtombstoned_client_rust_defaults"],
    apex_available: ["com.android.virt"],
}

rust_test {
    name: "libtombstoned_client_rust_test",
    defaults: ["libtombstoned_client_rust_defaults"],
    require_root: true,
    test_suites: ["device-tests"],
}

genrule {
    name: "libtombstoned_client_rust_bridge_code",
    tools: ["cxxbridge"],
    cmd: "$(location cxxbridge) $(in) >> $(out)",
    srcs: ["src/lib.rs"],
    out: ["libtombstoned_client_cxx_generated.cc"],
}
+153 −0
Original line number Diff line number Diff line
// Copyright 2022, The Android Open Source Project
//
// 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.

//! Rust wrapper for tombstoned client.

pub use ffi::DebuggerdDumpType;
use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use thiserror::Error;

/// Error communicating with tombstoned.
#[derive(Clone, Debug, Error, Eq, PartialEq)]
#[error("Error communicating with tombstoned")]
pub struct Error;

/// File descriptors for communicating with tombstoned.
pub struct TombstonedConnection {
    /// The socket connection to tombstoned.
    ///
    /// This is actually a Unix SOCK_SEQPACKET socket not a file, but the Rust standard library
    /// doesn't have an appropriate type and it's not really worth bringing in a dependency on `uds`
    /// or something when all we do is pass it back to C++ or close it.
    tombstoned_socket: File,
    /// The file descriptor for text output.
    pub text_output: Option<File>,
    /// The file descriptor for proto output.
    pub proto_output: Option<File>,
}

impl TombstonedConnection {
    unsafe fn from_raw_fds(
        tombstoned_socket: RawFd,
        text_output_fd: RawFd,
        proto_output_fd: RawFd,
    ) -> Self {
        Self {
            tombstoned_socket: File::from_raw_fd(tombstoned_socket),
            text_output: if text_output_fd >= 0 {
                Some(File::from_raw_fd(text_output_fd))
            } else {
                None
            },
            proto_output: if proto_output_fd >= 0 {
                Some(File::from_raw_fd(proto_output_fd))
            } else {
                None
            },
        }
    }

    /// Connects to tombstoned.
    pub fn connect(pid: i32, dump_type: DebuggerdDumpType) -> Result<Self, Error> {
        let mut tombstoned_socket = -1;
        let mut text_output_fd = -1;
        let mut proto_output_fd = -1;
        if ffi::tombstoned_connect_files(
            pid,
            &mut tombstoned_socket,
            &mut text_output_fd,
            &mut proto_output_fd,
            dump_type,
        ) {
            Ok(unsafe { Self::from_raw_fds(tombstoned_socket, text_output_fd, proto_output_fd) })
        } else {
            Err(Error)
        }
    }

    /// Notifies tombstoned that the dump is complete.
    pub fn notify_completion(&self) -> Result<(), Error> {
        if ffi::tombstoned_notify_completion(self.tombstoned_socket.as_raw_fd()) {
            Ok(())
        } else {
            Err(Error)
        }
    }
}

#[cxx::bridge]
mod ffi {
    /// The type of dump.
    enum DebuggerdDumpType {
        /// A native backtrace.
        #[cxx_name = "kDebuggerdNativeBacktrace"]
        NativeBacktrace,
        /// A tombstone.
        #[cxx_name = "kDebuggerdTombstone"]
        Tombstone,
        /// A Java backtrace.
        #[cxx_name = "kDebuggerdJavaBacktrace"]
        JavaBacktrace,
        /// Any intercept.
        #[cxx_name = "kDebuggerdAnyIntercept"]
        AnyIntercept,
        /// A tombstone proto.
        #[cxx_name = "kDebuggerdTombstoneProto"]
        TombstoneProto,
    }

    unsafe extern "C++" {
        include!("wrapper.hpp");

        type DebuggerdDumpType;

        fn tombstoned_connect_files(
            pid: i32,
            tombstoned_socket: &mut i32,
            text_output_fd: &mut i32,
            proto_output_fd: &mut i32,
            dump_type: DebuggerdDumpType,
        ) -> bool;

        fn tombstoned_notify_completion(tombstoned_socket: i32) -> bool;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::{io::Write, process};

    // Verify that we can connect to tombstoned, write something to the file descriptor it returns,
    // and notify completion, without any errors.
    #[test]
    fn test() {
        let connection =
            TombstonedConnection::connect(process::id() as i32, DebuggerdDumpType::Tombstone)
                .expect("Failed to connect to tombstoned.");

        assert!(connection.proto_output.is_none());
        connection
            .text_output
            .as_ref()
            .expect("No text output FD returned.")
            .write_all(b"test data")
            .expect("Failed to write to text output FD.");

        connection
            .notify_completion()
            .expect("Failed to notify completion.");
    }
}
+38 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022, The Android Open Source Project
 *
 * 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 "wrapper.hpp"

#include <android-base/unique_fd.h>

#include "tombstoned/tombstoned.h"

using android::base::unique_fd;

bool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,
                              int& proto_output_fd, DebuggerdDumpType dump_type) {
  unique_fd tombstoned_socket_unique, text_output_unique, proto_output_unique;

  bool result = tombstoned_connect(pid, &tombstoned_socket_unique, &text_output_unique,
                                   &proto_output_unique, dump_type);
  if (result) {
    tombstoned_socket = tombstoned_socket_unique.release();
    text_output_fd = text_output_unique.release();
    proto_output_fd = proto_output_unique.release();
  }

  return result;
}
+23 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022, The Android Open Source Project
 *
 * 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.
 */

#pragma once

#include <sys/types.h>
#include "tombstoned/tombstoned.h"

bool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,
                              int& proto_output_fd, DebuggerdDumpType dump_type);