App 的性能監(jiān)控

本文是<<iOS開發(fā)高手課>> 第十六篇學(xué)習(xí)筆記.

通常情況下访圃,App 的性能問題雖然不會(huì)導(dǎo)致 App 不可用厨幻,但依然會(huì)影響到用戶體驗(yàn)。

如果這個(gè)性能問題不斷累積,達(dá)到臨界點(diǎn)以后况脆,問題就會(huì)爆發(fā)出來饭宾。這時(shí),影響到的就不僅僅是用戶了格了,還有負(fù)責(zé) App 開發(fā)的我們看铆。

為了能夠主動(dòng)、高效地發(fā)現(xiàn)性能問題盛末,避免 App 質(zhì)量進(jìn)入無人監(jiān)管的失控狀態(tài)弹惦,我們就需要對(duì) App 的性能進(jìn)行監(jiān)控。

對(duì) App 的性能監(jiān)控悄但,主要是從線下和線上兩個(gè)維度展開棠隐。

Instruments

Instruments 是蘋果公司官方的性能監(jiān)控工具。被集成在 Xcode 里檐嚣,專門用來在線下進(jìn)行性能分析助泽。

Instruments 的功能非常強(qiáng)大,

  • Energy Log 就是用來監(jiān)控耗電量的嚎京,
  • Leaks 就是專門用來監(jiān)控內(nèi)存泄露問題的报咳,
  • Network 就是用來專門檢查網(wǎng)絡(luò)情況的,
  • Time Profiler 就是通過時(shí)間采樣來分析頁(yè)面卡頓問題的挖藏。
image.png

除了對(duì)各種性能問題進(jìn)行監(jiān)控外暑刃,還有以下兩大優(yōu)勢(shì):

  • Instruments 基于 os_signpost 架構(gòu),可以支持所有平臺(tái)膜眠。
  • Instruments 由于標(biāo)準(zhǔn)界面(Standard UI)和分析核心(Analysis Core)技術(shù)岩臣,使得我們可以非常方便地進(jìn)行自定義性能監(jiān)測(cè)工具的開發(fā)。當(dāng)你想要給 Instruments 內(nèi)置的工具換個(gè)交互界面宵膨,或者新創(chuàng)建一個(gè)工具的時(shí)候架谎,都可以通過自定義工具這個(gè)功能來實(shí)現(xiàn)。

從整體架構(gòu)來看辟躏,Instruments 包括 Standard UIAnalysis Core 兩個(gè)組件谷扣,它的所有工具都是基于這兩個(gè)組件開發(fā)的。而且捎琐,你如果要開發(fā)自定義的性能分析工具的話会涎,完全基于這兩個(gè)組件就可以實(shí)現(xiàn)。

線上性能監(jiān)控

對(duì)于線上性能監(jiān)控瑞凑,有兩個(gè)原則:

  • 監(jiān)控代碼不要侵入到業(yè)務(wù)代碼中末秃;
  • 采用性能消耗最小的監(jiān)控方案。

線上性能監(jiān)控籽御,主要集中在 CPU 使用率练慕、FPS 的幀率和內(nèi)存這三個(gè)方面惰匙。

CPU 使用率的線上監(jiān)控方法

App 作為進(jìn)程運(yùn)行起來后會(huì)有多個(gè)線程,每個(gè)線程對(duì) CPU 的使用率不同铃将。各個(gè)線程對(duì) CPU 使用率的總和项鬼,就是當(dāng)前 App 對(duì) CPU 的使用率。

在 iOS 系統(tǒng)中劲阎,你可以在 usr/include/mach/thread_info.h 里看到線程基本信息的結(jié)構(gòu)體绘盟,其中的 cpu_usage 就是 CPU 使用率哪工。結(jié)構(gòu)體的完整代碼如下所示:

struct thread_basic_info {
  time_value_t    user_time;     // 用戶運(yùn)行時(shí)長(zhǎng)
  time_value_t    system_time;   // 系統(tǒng)運(yùn)行時(shí)長(zhǎng)
  integer_t       cpu_usage;     // CPU 使用率
  policy_t        policy;        // 調(diào)度策略
  integer_t       run_state;     // 運(yùn)行狀態(tài)
  integer_t       flags;         // 各種標(biāo)記
  integer_t       suspend_count; // 暫停線程的計(jì)數(shù)
  integer_t       sleep_time;    // 休眠的時(shí)間
};

因?yàn)槊總€(gè)線程都會(huì)有這個(gè) thread_basic_info 結(jié)構(gòu)體奥此,只需要定時(shí)(比如,將定時(shí)間隔設(shè)置為 2s)去遍歷每個(gè)線程程奠,累加每個(gè)線程的 cpu_usage 字段的值,就能夠得到當(dāng)前 App 所在進(jìn)程的 CPU 使用率了距境。實(shí)現(xiàn)代碼如下:


+ (integer_t)cpuUsage {
    thread_act_array_t threads; //int 組成的數(shù)組比如 thread[1] = 5635
    mach_msg_type_number_t threadCount = 0; //mach_msg_type_number_t 是 int 類型
    const task_t thisTask = mach_task_self();
    //根據(jù)當(dāng)前 task 獲取所有線程
// task_threads 方法能夠取到當(dāng)前進(jìn)程中的線程總數(shù) threadCount 和所有線程的數(shù)組 threads申尼。
    kern_return_t kr = task_threads(thisTask, &threads, &threadCount);
    
    if (kr != KERN_SUCCESS) {
        return 0;
    }
    
    integer_t cpuUsage = 0;
   
// 我們就可以通過遍歷這個(gè)數(shù)組來獲取單個(gè)線程的基本信息。其中垫桂,線程基本信息的結(jié)構(gòu)體是 thread_basic_info_t师幕,這個(gè)結(jié)構(gòu)體里就包含了我們需要的 CPU 使用率的字段 cpu_usage。然后诬滩,我們累加這個(gè)字段就能夠獲取到當(dāng)前的整體 CPU 使用率霹粥。
    for (int i = 0; i < threadCount; i++) {
        
        thread_info_data_t threadInfo;
        thread_basic_info_t threadBaseInfo;
        mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
        
        if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) {
            // 獲取 CPU 使用率
            threadBaseInfo = (thread_basic_info_t)threadInfo;
            if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) {
                cpuUsage += threadBaseInfo->cpu_usage;
            }
        }
    }
    assert(vm_deallocate(mach_task_self(), (vm_address_t)threads, threadCount * sizeof(thread_t)) == KERN_SUCCESS);
    return cpuUsage;
}

FPS 線上監(jiān)控方法

FPS 是指圖像連續(xù)在顯示設(shè)備上出現(xiàn)的頻率。FPS 低疼鸟,表示 App 不夠流暢蒙挑,還需要進(jìn)行優(yōu)化。

和前面對(duì) CPU 使用率和內(nèi)存使用量的監(jiān)控不同愚臀,iOS 系統(tǒng)中沒有一個(gè)專門的結(jié)構(gòu)體忆蚀,用來記錄與 FPS 相關(guān)的數(shù)據(jù)矾利。但是,對(duì) FPS 的監(jiān)控也可以比較簡(jiǎn)單的實(shí)現(xiàn):通過注冊(cè) CADisplayLink 得到屏幕的同步刷新率馋袜,記錄每次刷新時(shí)間男旗,然后就可以得到 FPS。具體的實(shí)現(xiàn)代碼如下:

- (void)start {
    self.dLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(fpsCount:)];
    [self.dLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

// 方法執(zhí)行幀率和屏幕刷新率保持一致
- (void)fpsCount:(CADisplayLink *)displayLink {
    if (lastTimeStamp == 0) {
        lastTimeStamp = self.dLink.timestamp;
    } else {
        total++;
        // 開始渲染時(shí)間與上次渲染時(shí)間差值
        NSTimeInterval useTime = self.dLink.timestamp - lastTimeStamp;
        if (useTime < 1) return;
        lastTimeStamp = self.dLink.timestamp;
        // fps 計(jì)算
        fps = total / useTime; 
        total = 0;
    }
}

內(nèi)存使用量的線上監(jiān)控方法

通常情況下欣鳖,我們?cè)讷@取 iOS 應(yīng)用內(nèi)存使用量時(shí)察皇,都是使用 task_basic_info 里的 resident_size 字段信息。但這樣獲得的內(nèi)存使用量和 Instruments 里看到的相差很大泽台。后來什荣,在 2018 WWDC Session 416 iOS Memory Deep Dive,蘋果公司介紹說 phys_footprint 才是實(shí)際使用的物理內(nèi)存怀酷。

struct task_vm_info {
  mach_vm_size_t  virtual_size;       // 虛擬內(nèi)存大小
  integer_t region_count;             // 內(nèi)存區(qū)域的數(shù)量
  integer_t page_size;
  mach_vm_size_t  resident_size;      // 駐留內(nèi)存大小
  mach_vm_size_t  resident_size_peak; // 駐留內(nèi)存峰值

  ...

  /* added for rev1 */
  mach_vm_size_t  phys_footprint;     // 物理內(nèi)存
  ...

開發(fā)一款自定義 Instruments 工具

Instruments 通過提供 os_signpost API 的方式使得開發(fā)者監(jiān)控自定義的性能指標(biāo)時(shí)更方便稻爬,從而解決了在此之前只能通過重新建設(shè)工具來完成的問題。并且蜕依,Instruments 是通過 XML 標(biāo)準(zhǔn)數(shù)據(jù)接口解耦展示和數(shù)據(jù)分析

主要包括以下這幾個(gè)步驟:

  • 在 Xcode 中桅锄,點(diǎn)擊 File > New > Project;
  • 在彈出的 Project 模板選擇界面样眠,將其設(shè)置為 macOS友瘤;
  • 選擇 Instruments Package,點(diǎn)擊后即可開始自定義工具的開發(fā)了檐束。

創(chuàng)建之后僅有一個(gè)源文件(.instrpkg)

運(yùn)行后會(huì)彈出一個(gè) Instruments 頁(yè)面,在菜單欄 -> Instruments -> Preferences -> Packages

開發(fā)過程主要是對(duì) instrpkg 文件的配置工作辫秧。這些配置工作中最主要的是要完成 Standard UI 和 Analysis Core 的配置。

蘋果公司還提供了大量的代碼片段被丧,幫助你進(jìn)行個(gè)性化的配置盟戏。你可以查看官方指南中的詳細(xì)教程:https://help.apple.com/instruments/developer/mac/current/

配置 instrpkg 文件

Xcode提供的instrpkg模板中注釋很多,核心的代碼沒有多少晚碾。

但是基本上可以知道這個(gè)代碼是 XML 格式的抓半,通過不同的標(biāo)簽標(biāo)示不同的功能,package 標(biāo)簽標(biāo)示一個(gè)包格嘁,緊接著是其子標(biāo)簽:id笛求、title 與 owner 等等。

<?xml version="1.0" encoding="UTF-8" ?>
<package>
    <id>com.forping.Test</id>
    <title>Test</title>
    <owner>
        <name>forping</name>
    </owner>
    
    <!-- 可以理解成一個(gè)數(shù)據(jù)來源 -->
    <os-signpost-interval-schema>
        <id>json-parse</id>
        <title>JSON Decode</title>
        
        <!-- 這三個(gè)是與項(xiàng)目中代碼一一對(duì)應(yīng) -->
        <subsystem>"com.forping.forping"</subsystem>
        <category>"jsonDecode"</category>
        <name>"Parsing"</name>
        
        <!-- 開始匹配-->
        <start-pattern>
            <message>"Parsing started"</message>
        </start-pattern>
        
        <!-- 結(jié)束匹配-->
        <end-pattern>
            <message>"Parsing end SIZE:" ?data-size-value</message>
        </end-pattern>
        
        <!-- 表中的一列 -->
        <column>
            <!-- 助記符標(biāo)識(shí), 在 graph 與 list 中只認(rèn)這個(gè)標(biāo)識(shí) -->
            <mnemonic>data-size</mnemonic>
            <title>JSON Data Size</title>
            <!-- 數(shù)據(jù)的類型 size-in-bytes -->
            <type>size-in-bytes</type>
            <!-- 顯示 data-size 的值  -->
            <expression>?data-size-value</expression>
        </column>
        
        <!-- https://help.apple.com/instruments/developer/mac/current/#/dev66257045 -->
        <column>
            <mnemonic>impact</mnemonic>
            <title>Impact</title>
            <type>event-concept</type>
            <expression>(if (&gt; ?data-size-value 80) then "High" else "Low")</expression>
        </column>
        
    </os-signpost-interval-schema>
    
    <!-- 導(dǎo)入 tick 模塊 可以使用  tick作為數(shù)據(jù)來源  相當(dāng)于 `import` -->
<!--    <import-schema>tick</import-schema>-->
    
    <!-- 開始構(gòu)建一個(gè) instrument  -->
    <instrument>
        <id>com.forping.ticksinstrument</id>
<!--        在 instrument中的title-->
        <title>FPTicks</title>
        <category>Behavior</category>
        <purpose>tickDemo</purpose>
        <icon>Generic</icon>
        
        <!-- 創(chuàng)建一個(gè)表, 這個(gè)表中使用到了 `tick`  -->
        <create-table>
            <id>json-parse</id>
            <!-- os-signpost-interval-schema 的 id -->
            <schema-ref>json-parse</schema-ref>
        </create-table>
        
        <!-- 軌道視圖 為您的儀器定義要繪制的圖形(可選) -->
        <graph>
            <title>JSON Decode</title>
            <lane>
                <title>JSON Analyz</title>
                <table-ref>json-parse</table-ref>
                
                <!-- 繪圖糕簿、繪圖模板或直方圖元素 -->
                <plot>
                    <value-from>data-size</value-from>
                    <color-from>impact</color-from>
                </plot>
            </lane>
        </graph>
        
        
        <!-- 詳情視圖 - 為您的儀器定義至少一個(gè)詳細(xì)視圖 -->
        <list>
            <title>data-info</title>
            <table-ref>json-parse</table-ref>
            <column>data-size</column>
            <column>impact</column>
            <column>duration</column>
        </list>
    </instrument>

    <!-- Instruments Developer Help: https://help.apple.com/instruments/developer/mac/current/ -->

    <!-- MARK: Schema Definitions -->
    <!-- Define point and interval schemas needed to represent the input and output tables your package will use. -->
    <!-- Two kinds are available: schemas with automatically generated modelers, and schemas that require custom modelers -->
    <!--   Generated modelers: 'os-log-point-schema', 'os-signpost-interval-schema', 'ktrace-point-schema', 'ktrace-interval-schema' -->
    <!--   Custom modeler required: 'point-schema', 'interval-schema' -->
    <!-- To use existing schemas from other packages, declare 'import-schema' elements -->

    <!-- MARK: Modeler Declarations -->
    <!-- If there are schemas defined that require a custom modeler, each can be declared with a 'modeler' element -->
    <!-- Modelers are based on CLIPS rules and may define 1..n output schemas, each requiring 1..n input schemas -->

    <!-- MARK: Instrument Definitions -->
    <!-- Instruments record and display data, creating concrete table requirements that instance modelers and data streams. -->
    <!-- Any number of 'instrument' elements can be defined; each instrument should provide a cohesive graph and detail experience. -->

    <!-- MARK: Embed Templates -->
    <!-- Templates may be included and represent a collection of tools configured for a specific tracing workflow -->
    <!-- Each 'template' element specifies the relative path to a .tracetemplate file in the project -->
    <!-- To create a template: start with a blank document, configure with instruments desired, and choose "File -> Save as Template" -->
</package>

這就是核心實(shí)現(xiàn) Instruments 功能的代碼了探入,詳細(xì)解釋如下:

  1. 使用了 Instrument 之后依舊需要添加對(duì)應(yīng)的標(biāo)識(shí)、標(biāo)題等基本信息懂诗。
  2. 需要?jiǎng)?chuàng)建一個(gè)對(duì)這個(gè)自定義的 Instrument 需要有一張對(duì)應(yīng)的表(table)蜂嗽,故需要使用 create-table,值得注意的是這個(gè)表所需要的數(shù)據(jù)是直接來自于 tick schema。
  3. 開始創(chuàng)建一個(gè)軌道視圖殃恒,這個(gè)軌道視圖的數(shù)據(jù)來自 tick-table 這張表植旧,由于這張表引用系統(tǒng)的 tick schema辱揭,tick 中有一個(gè) time 屬性,所以可以直接使用這個(gè)時(shí)間戳字段病附。
  4. 詳情視圖问窃,使用 list 標(biāo)簽主要是在詳情視圖中顯示數(shù)據(jù)的。這個(gè) list 相當(dāng)于我們開發(fā)中的 UITableView完沪,tick-table 相當(dāng)于數(shù)據(jù)源(dataSource)域庇。

使用方法
選擇 Blank , 點(diǎn)擊新視圖右側(cè)的 + 號(hào),選擇我們 instrument 標(biāo)題的 title

Analysis Core

如果你想要更好地進(jìn)行個(gè)性化定制,就還需要再了解 Instruments 收集和處理數(shù)據(jù)的機(jī)制覆积,也就是分析核心(Analysis Core )的工作原理听皿。Analysis Core 收集和處理數(shù)據(jù)的過程,可以大致分為三步:

  • 處理我們配置好的各種數(shù)據(jù)表宽档,并申請(qǐng)存儲(chǔ)空間 store尉姨;

  • store 去找數(shù)據(jù)提供者,如果不能直接找到雌贱,就會(huì)通過 Modeler 接收其他 store 的輸入信號(hào)進(jìn)行合成啊送;

  • store 獲得數(shù)據(jù)源后偿短,會(huì)進(jìn)行 Binding Solution 工作來優(yōu)化數(shù)據(jù)處理過程欣孤。

在通過 store 找到的這些數(shù)據(jù)提供者中,對(duì)開發(fā)者來說最重要的就是 os_signpost昔逗。

os_signpost 的主要作用降传,是讓你可以在程序中通過編寫代碼來獲取數(shù)據(jù)。你可以在工程中的任何地方通過 os_signpost API 勾怒,將需要的數(shù)據(jù)提供給 Analysis Core婆排。

模擬代碼

os_log_t parsingLog = os_log_create("com.forping.forping", "jsonDecode");

os_signpost_id_t signid = os_signpost_id_generate(parsingLog);
        
os_signpost_interval_begin(parsingLog, signid, "Parsing started");
// 模擬耗時(shí)操作
[self jsonDecode];
        
os_signpost_interval_end(parsingLog, signid, "Parsing end");

運(yùn)行效果

image.png

上面的代碼,主要是獲取項(xiàng)目中耗時(shí)操作的開始與結(jié)束的笔链。其中在結(jié)束的時(shí)候會(huì)匹配出項(xiàng)目中的元數(shù)據(jù):解析字符的大小段只。這里主要使用的就是 CLIPS 語(yǔ)言的變量。
接著就是 column, 這個(gè)標(biāo)簽為 shema 定義一些字段鉴扫, schema 是一個(gè)數(shù)據(jù)庫(kù)赞枕。其中這個(gè)是數(shù)據(jù)庫(kù)中有兩個(gè) key:data-sizeimpact,其中 impact 是由 data-size-value 的值決定的坪创,大于 80 時(shí)值是 High炕婶, 否則為 Low

可以很清楚的看到每次 JSON 解析的開始與結(jié)束莱预,以及執(zhí)行所花的時(shí)間柠掂。
在實(shí)際開發(fā)中可能還會(huì)同時(shí)選中其它的調(diào)試模塊,比如 Time Profiler依沮、內(nèi)存檢測(cè) 等涯贞,這樣能很好的全方位的分析當(dāng)前的運(yùn)行環(huán)境以及運(yùn)行狀態(tài)枪狂。

其他示例

官方示例

蘋果公司在 WWDC 2018 Session 410 Creating Custom Instruments 里提供了一個(gè)范例:https://developer.apple.com/videos/play/wwdc2018/410
通過 os_signpost API 將圖片下載的數(shù)據(jù)提供給 Analysis Core 進(jìn)行監(jiān)控觀察。這個(gè)示例在 App 的代碼如下所示:

//os_signpost 的 begin 和 end 需要成對(duì)出現(xiàn)宋渔。
os_signpost(.begin, log: parsinglog, name:"Parsing", "Parsing started SIZE:%ld", data.count)
// Decode the JSON we just downloaded
let result = try jsonDecoder.decode(Trail.self, from: data)
os_signpost(.end, log: parsingLog, name:"Parsing", "Parsing finished")

上面這段代碼就是使用 os_signpost 的 API 獲取程序里的數(shù)據(jù)摘完。

Instruments 是如何通過配置數(shù)據(jù)表來使用這些數(shù)據(jù)的。配置的數(shù)據(jù)表的 XML 設(shè)計(jì)如下所示:

<os-signpost-interval-schema>
<id>json-parse</id>
<title>Image Download</title>
<subsystem>"com.apple.trailblazer</subsystem>
<category>"Networking</category>
<name>"Parsing"</name>
<start-pattern>
<message>"Parsing started SIZE:" ?data-size</message> 
</start-pattern>
<column>
<mnemonic>data-size</mnemonic>
<title>JSON Data Size</title>
<type>size-in-bytes</type>
<expression>?data-size</expression>
</column>
</os-signpost-interval-schema>

配置數(shù)據(jù)表是要對(duì)數(shù)據(jù)輸出進(jìn)行可視化配置傻谁,從而可以將代碼中的數(shù)據(jù)展示出來孝治。如下圖所示,就是對(duì)下載圖片大小監(jiān)控的效果审磁。


image.png

參考鏈接:

https://juejin.cn/post/6844903854065057806

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谈飒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子态蒂,更是在濱河造成了極大的恐慌杭措,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钾恢,死亡現(xiàn)場(chǎng)離奇詭異手素,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瘩蚪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門泉懦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人疹瘦,你說我怎么就攤上這事崩哩。” “怎么了言沐?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵邓嘹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我险胰,道長(zhǎng)汹押,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任起便,我火速辦了婚禮棚贾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缨睡。我一直安慰自己鸟悴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布奖年。 她就那樣靜靜地躺著细诸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陋守。 梳的紋絲不亂的頭發(fā)上震贵,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天利赋,我揣著相機(jī)與錄音,去河邊找鬼猩系。 笑死媚送,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寇甸。 我是一名探鬼主播塘偎,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼拿霉!你這毒婦竟也來了吟秩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绽淘,失蹤者是張志新(化名)和其女友劉穎涵防,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沪铭,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壮池,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杀怠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椰憋。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驮肉,靈堂內(nèi)的尸體忽然破棺而出熏矿,到底是詐尸還是另有隱情已骇,我是刑警寧澤离钝,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站褪储,受9級(jí)特大地震影響卵渴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鲤竹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一浪读、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辛藻,春花似錦碘橘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至氮墨,卻和暖如春纺蛆,著一層夾襖步出監(jiān)牢的瞬間吐葵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工桥氏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留温峭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓字支,卻偏偏與公主長(zhǎng)得像凤藏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子堕伪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345