< 返回版块

ChaosBot 发表于 2018-10-30 11:33

Tags:rustnews

「博文」查找并修复Hyper应用中的内存泄漏

作者对其应用的下一个版本进行负载测试,使用wrk工具发送请求几秒之后,后端内存占用了1.3GB,内存占用的增长速度大约每秒50MB。

初步调试:

开始使用heaptrack工具,发现内存只分配了8KB,问题出在了heaptrack工具和jemalloc并不搭,所以换用Rust的system分配器。

use std::alloc::System;

#[global_allocator]
static A: System = System;

这样就可以用heaptrack来检测到内存泄漏了。然而还是没有得到什么有用的信息。

改变策略:

有一个办法,可以通过逐步删除部分程序来确定内存泄漏的位置。那么有没有这样一个工具来代替开发者手工完成?C-Reduce行不行?可惜的是,他们发现C-Reduce无法支持Rust。所以只能想办法徒手实现了一个最小版本的应用示例。

#![feature(alloc_system)]
#![feature(allocator_api)]
#[global_allocator]
static GLOBAL: std::alloc::System = std::alloc::System;

#[macro_use]
extern crate failure;
extern crate future_utils;
extern crate hyper;
extern crate tokio;

use failure::Error;
use future_utils::{BoxSendFuture, FutureExt};
use hyper::service::Service;
use hyper::Body;
use std::time::Duration;
use tokio::prelude::*;

fn main() {
    let server = hyper::Server::bind(&"127.0.0.1:31337".parse().unwrap())
        .serve(|| Ok::<_, Box<std::error::Error + Send + Sync>>(Server));
    let mut rt = tokio::runtime::Runtime::new().unwrap();
    rt.spawn(server.map_err(move |_err| {}));
    println!("server started");
    ::std::thread::sleep(Duration::from_secs(10));
    println!("shutting down");
    rt.shutdown_now().wait().unwrap();
}

#[derive(Debug, Fail)]
#[fail(display = "myfail")]
pub struct MyFail;

pub struct Server;
impl Service for Server {
    type ReqBody = Body;
    type ResBody = Body;
    type Error = hyper::Error;
    type Future = BoxSendFuture<hyper::Response<Self::ResBody>, Self::Error>;

    fn call(&mut self, _request: hyper::Request<Self::ReqBody>) -> Self::Future {
        Err(Error::from(::MyFail))
            .into_future()
            .or_else(|err: Error| Ok(err.downcast::<::MyFail>().unwrap().into_future()) // removing this gets rid of the leak
            .map(|_| ::hyper::Response::new(Body::empty()))
            .into_send_boxed()
    }
}

最终发现,是在转换错误类型的时候调用into_future()的时候发生了泄漏。然后他们使用cargo-geiger工具,去发现导致这次泄漏的unsafe代码。

最终他们定位到了问题所在:

pub(crate) fn downcast<T: Fail>(self) -> Result<T, ErrorImpl> {
        let ret: Option<T> = self.failure().downcast_ref().map(|fail| {
            unsafe {
                // drop the backtrace
                let _ = ptr::read(&self.inner.backtrace as *const Backtrace);
                // read out the fail type
                ptr::read(fail as *const T)
            }
        });
        match ret {
            Some(ret) => {
                // forget self (backtrace is dropped, failure is moved
                mem::forget(self);
                Ok(ret)
            }
            _ => Err(self)
        }
    }

是failure库里的downcast方法使用了mem::forget。这就导致了Error::downcast并没有把Box<Inner<Fail>>给drop掉,而仅仅是drop了backtrace。

之前有一个PR补丁修复了此泄漏漏洞 failure-pr-262

// deallocate the box without dropping the inner parts
#[cfg(has_global_alloc)] {
    use std::alloc::{dealloc, Layout};
    unsafe {
        let layout = Layout::for_value(&*self.inner);
        let ptr = Box::into_raw(self.inner);
        dealloc(ptr as *mut u8, layout);
    }
}

但是在Rust 1.18之前的版本还存在泄漏问题,可见作者用的还是老版本。

因此,他的同事给Failure提交了一个PR,修复此问题,现在已经被合并到failure中。

pub(crate) fn downcast<T: Fail>(self) -> Result<T, ErrorImpl> {
    if self.failure().__private_get_type_id__() == TypeId::of::<T>() {
        let ErrorImpl { inner } = self;
        let casted = unsafe { Box::from_raw(Box::into_raw(inner) as *mut Inner<T>) };
        let Inner { backtrace:_, failure } = *casted;
        Ok(failure)
    } else {
        Err(self)
    }
}

Read More


「项目」sled 发布 0.16版本

关键字:[embedded, database, concurrent, b-tree, zero-copy]

sled是一款Rust实现的现代化嵌入式数据库

新的版本更加可靠,且实现了零Copy读取,Rust的所有权模型让引用安全地返回给缓存而无需防御性的拷贝。

sled


「GUI框架」Azul:面向IMGUI的免费功能性GUI框架

关键字: [azul, gui, imgui, webrender, mvvm]

支持用Rust编写桌面软件,基于Mozilla WebRender渲染引擎。

目前还在完善中,期望特点(也就是说,当前这些功能并非全部支持):

  • 跨平台
  • IMGUI (Immediate Mode GUI )/ MVVM 编程模型
  • 基于Dom的无状态组件
  • CSS风格的样式引擎,支持多种通用CSS属性
  • 基于Flexbox的布局
  • 内置标准控件
  • 自定义widgets
  • SVG渲染引擎,2D绘图助手 (lines, circles, rects, etc.)
  • OpenGL集成
  • Async I/O
  • 可选的集成日志和错误报告辅助
  • 独立二进制部署,最小二进制大小(5MB)
  • CPU使用率(0-4%),内存使用率(总共约50MB)
  • 快速重绘时间(0.5~4ms),包括了高效的状态缓存

azul.rs


「异步Web框架」Gotham 0.3发布

关键字: [Gotham, async, web-framwork, tokio, hyper]

特点:

  • 基于新的tokio库
  • 采用hyper 0.12和新的http库
  • 内部路由算法改进
  • 自定义Gotham使用tokio运行时
  • 通过tokio-fs提供异步静态文件
  • 更多性能和可用性改进

Read More


「项目」RESSA:Rusty ECMAScript语法分析器

关键字: [RESSA, ECMAScript, JavaScript, parse]

该项目是用于把js文本解析为基于ESTREE的AST

RESSA


「Web框架」使用actix-web制作一个验证用户身份的微服务 Part 2

关键字: [actix-web, microservice, diesel, auth]

前文索引

Read More


「博文」如何使用Cargo构建Qt应用

关键字: [Qt, cargo]

目的是想让Qt的安装和运行更加简单

Read More


每日新闻订阅地址:

欢迎通过GitHub issues投稿。

评论区

写评论

还没有评论

1 共 0 条评论, 1 页