ART Runtime 创建(二)--启动参数

Android 7.0 aosp_shamu-userdebug

一. 启动时获取的参数

AndroidRuntime::startVm(/framework/bae/cpre/jni/AndroidRuntime.cpp)方法会首先获取大量系统属性,并将这些系统属性转化为实际的启动参数,下面是获取的系统属性以及对应的启动参数:

  • dalvik.vm.checkjni(ro.kernel.android.checkjni)

真实值:无
默认为false, 如果为true, 则添加参数-Xcheck:jni

  • dalvik.vm.execution-mode

真实值:无
默认为KEMDefault, 有四个取值:KEMDefault, kEMIntPortable, kEMIntFast, kEMJitCompiler. 如果不是kEMDefault,则相应的参数为:-Xint:portable, -Xint:fast, -Xint:jit

  • dalvik.vm.stack-trace-file

真实值:/data/anr/traces.txt
参数的形式为:-Xstacktracefile:/data/anr/traces.txt

  • dalvik.vm.jniopts

真实值:没有设置
如果有设置的话,参数形式为:Xjnipots:...

  • {exit, runtime_exit}

Hook Runtime的exit()runtime_exit(int code)函数

  • {vfprintf, runtime_vfprintf}

Hook Runtime的fprintf()runtime_vfprintf(FILE* fp, const char* format, va_list ap)函数

  • {sensitiveThread, runtime_isSensitiveThread()}

Hook Runtime的sensitiveThread()runtime_isSensitiveThread()函数

  • 直接添加参数-verbose:gc
  • dalvik.vm.heapstartsize

真实值:8m
参数的形式为:-Xms8m

  • dalvik.vm.heapsize

真实值:512m
参数的形式为:-Xmx512m

  • dalvik.vm.heapgrowthlimit

真实值:256m
参数形式为:-XX:HeapGrowthLimit=256m

  • dalvik.vm.heapminfree

真实值:512k,
参数形式为:-XX:HeapMinFree=512k

  • dalvik.vm.heapmaxfree

真实值:8m
参数形式为:-XX:HeapMaxFree=8m

  • dalvik.vm.heaputilization

真实值:0.75
参数形式为:-XX:HeapTargetUtilization=0.75

  • dalvik.vm.usejit

真实值:true
参数形式为:-Xusejit:true

  • dalvik.vm.jitmaxsize

真实值是:无
如果设置则参数形式为:-Xjitmaxsize:${dalvik.vm.jitmaxsize}

  • dalvik.vm.jitinitialize

真实值:无,
如果设置参数形式为:-Xjitinitialize:${dalvik.vm.jitinitialize}

  • dalvik.vm.jitthreshold

真实值:无
如果设置参数形式为:-Xjitthreshold:..

  • dalvik.vm.usejitprofiles

真实值:true,
参数形式为:-Xjitsaveprofilinginfo

  • dalvik.vm.jitprithreadweight

真实值:无
如果设置,参数的形式为:-Xjitprithreadweight

  • dalvik.vm.jittransitionweight

真实值:无
如果设置,参数的形式为:-Xjittransitionweight:${dalvik.vm.jittransitionweight}

  • ro.config.low_ram

真实值:无
如果设置,参数形式为:-XXLowMemoryMode

  • dalvik.vm.gctype

真实值:无
如果设置,参数形式为:-Xgc:${dalvik.vm.gctype}

  • dalvik.vm.backgroundgctype

真实值:无
如果设置,参数形式为:-XX:BackgroundGC=${dalvik.vm.backgroundgctype}

  • 直接添加参数-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y
  • dalvik.vm.lockprof.threshold

真实值:500
参数形式为:-Xlockprofthreshold:500

  • vold.decrypt

trigger_restart_framework
因为不等于trigfer_restart_min_framework1,所以还要获取属性dalvik.vm.image-dex2oat-filter, 系统中并未设置该值,如果设置了,参数的形式为:--compiler-filter=${dalvik.vm.image-dex2oat-filter}, -Ximage-compiler-option

  • 直接添加参数Ximage-compiler-option

  • 直接添加参数--compiled-classes=/system/etc/preloaded-classes

  • 如果文件/system/etc/compiled-classes存在(实际存在),则添加参数:-Ximage-compiler-option, --compiled-classes=/system/etc/compiled-classes

  • dalvik.vm.imgae-dex2oat-flags

真实值:无
如果设置,参数形式为:-Ximage-compile-option, ${image-dex2oat-flags}

  • dalvik.vm.dex2oat-Xms

真实值:64m
参数形式为:-Xcompiler-option, --runtime-arg, -Xcompiler-option, -Xms64m

  • dalvik.vm.dex2oat-Xmx

真实值:512m
参数形式为:-Xcompiler-option, --runtime-arg, -Xcompiler-option, -Xms512m

  • dalvik.vm.dex2oat-filter

真实值:无
如果设置了,参数的形式为:-Xcompiler-option, --runtime-arg, -Xcompiler-option, --compiler-filter=${dalvik.vm.dex2oat-filter}

  • dalvik.vm.dex2oat-threads

真实值:无
如果设置了,参数形式为:-Xcompiler-option, --runtime-arg, -Xcompiler-option, -j${dalvik.vm.dex2oat-threads}

  • dalvik.vm.image-dex2oat-threads

真实值:无
如果设置了,参数形式为:-Ximage-compiler-option, --runtime-arg, -Ximage-compiler-option, -j${dalvik.vm.image-dex2oat-threads}

  • dalvik.vm.isa.arm.variant

真实值:krait
参数最终形式为:-Ximage-compiler-option, --runtime-arg, -Ximage-compiler-option, --instruction-set-variant=krait , -Xcompiler-option, --runtime-arg, -Xcompiler-option, --instruction-set-variant=krait

  • dalvik.vm.isa.arm.features

真实值:default
参数形式为:-Ximage-compiler-option, --runtime-arg, -Ximage-compiler-option, --instruction-set-features=default, -Xcompiler-option, --runtime-arg, -Xcompiler-option, instruction-set-variant-features=default

  • dalvik.vm.dex2oat-flags

真实值:无
如果设置,参数形式为:-Xcompiler-option, [dex2oat-flags]

  • dalvik.vm.extra-opts

真实值:无
如果设置,参数形式为:[extra-opts]

  • 读取Locale,然后添加参数-Duser.locale=zh-Hans-CN
  • ro.debuggable

真实值:1,
如果ro.debuggable=1同时dalvik.vm.method-trace=true(实际为false),则添加参数-Xmethod-trace, -Xmethod-trace-file:[dalvik.vm.method-trace-file], -Xmethod-trace-file-size:[dalvik.vm.method-trace-file-siz], 如果dalvik.vm.method-trace-stream=true, 添加参数-Xmethod-trace-stream(实际都没有添加)

  • ro.dalvik.vm.native.bridge

真实值:0
如果值不为0, 则参数为:-XX:NativeBridge=[ro.dalvik.vm.native.bridge]

  • ro.product.cpu.abilist32

真实值:[armeabi-v7a,armeabi]
参数形式为:--cpu-abilist=[armeabi-v7a,armeabi]

  • dalvik.vm.zygote.max-boot-retry

真实值:无
如果设置, 最终形式为:-Xzygote-max-boot-retry=[dalvik.vm.zygote.max-boot-retry]

  • debug.generate-debug-info

真实值:无
如果值为true, 添加参数:-Xcompiler-option, --generate-debug-info, -Ximage-compiler-option, --generate-debug-info

  • ro.build.fingerprint

真实值:Android/aosp_shamu/shamu:7.0/NBD91U/xx12231922:userdebug/test-keys
参数形式为:-Xfingerprint:Android/aosp_shamu/shamu:7.0/NBD91U/xx12231922:userdebug/test-keys

二. 实际传入的参数

{
  {`-Xstacktracefile:/data/anr/traces.txt`, NULL}, //{optionString, extraInfo}
  {`exit`, runtime_exit},
  {`vfprintf`, `runtime_vfprintf`},
  {`sensitiveThread`, `runtime_isSensitiveThread`},
  {`-verbose:gc`, NULL},
  {`-Xms8m`, NULL},
  {`-Xmx512m`, NULL},
  {`-XX:HeapGrowthLimit=256m`,NULL},
  {`-XX:HeapMinFree=512k`, NULL},
  {`-XX:HeapMaxFree=8m`,NULL},
  {`-XX:HeapTargetUtilization=0.75`,NULL},
  {`-Xusejit:true`, NULL},
  {`-Xjitsaveprofilinginfo`,NULL},
  {`-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y`, NULL},
  {`-Xlockprofthreshold:500`,NULL},
  {`-Ximage-compiler-option`,NULL},
  {`--compiled-classes=/system/etc/preloaded-classes`,NULL},
  {`-Ximage-compiler-option`,NULL},
  {`--compiled-classes=/system/etc/compiled-classes`,NULL},
  {`-Xcompiler-option`,NULL},
  {`--runtime-arg`,NULL},
  {`-Xcompiler-option`,NULL},
  {`-Xms64m`,NULL},
  {`-Xcompiler-option`,NULL},
  {`--runtime-arg`,NULL},
  {`-Xcompiler-option`,NULL},
  {`-Xmx512m`,NULL},
  {`-Ximage-compiler-option`,NULL},
  {`--runtime-arg`,NULL},
  {`-Ximage-compiler-option`,NULL},
  {`--instruction-set-variant=krait`,NULL},
  {`-Xcompiler-option`,NULL},
  {`--runtime-arg`,NULL},
  {`-Xcompiler-option`,NULL},
  {`--instruction-set-variant=krait`,NULL},
  {`-Ximage-compiler-option`,NULL},
  {`--runtime-arg`,NULL},
  {`-Ximage-compiler-option`,NULL},
  {`--instruction-set-features=default`,NULL},
  {`-Xcompiler-option`,NULL},
  {`--runtime-arg`,NULL},
  {`-Xcompiler-option`,NULL},
  {`--instruction-set-features=default`,NULL},
  {`-Duser.locale=zh-Hans-CN`,NULL},
  {`--cpu-abilist=[armeabi-v7a,armeabi]`,NULL},
  {`-Xfingerprint:Android/aosp_shamu/shamu:7.0/NBD91U/xx12231922:userdebug/test-keys`, NULL}
}

三. 存储参数的数据结构和方法

3.1 存储参数的数据结构

typedef struct JavaVMInitArgs {
  jint          version;
  jint          nOptions;
  JavaVMOption* options;
  jboolean      ignoreUnrecognized
}

typedef struct JavaVMOption {
  const char* optionString;
  void*       extractInfo;
}

typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions

3.2 最终传入的数据结构

JavaVMInitArgs initArgs;
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;

3.3 添加参数的方法

void AndroidRuntime::addOption(const char* optionString, void* extraInfo)
{
    JavaVMOption opt;
    opt.optionString = optionString;
    opt.extraInfo = extraInfo;
    mOptions.add(opt);
}

四. 解析参数流程

4.1 JNI_CreateJavaVM

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
  ...
  RuntimeOptions options;
  for (int i = 0; i < args->nOptions; ++i) {
      JavaVMOption* option = &args->options[i];
      options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
  }
  bool ignore_unrecognized = args->ignoreUnrecognized;
  if (!Runtime::Create(options, ignore_unrecognized)) {
      return JNI_ERR;
  }
  ...
}

4.2 Runtime::Create()

bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
  RuntimeArgumentMap runtime_options;

  return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) &&
      Create(std::move(runtime_options));
}

4.3 Runtime::ParseOptions

bool Runtime::ParseOptions(const RuntimeOptions& raw_options,
                           bool ignore_unrecognized,
                           RuntimeArgumentMap* runtime_options) {
  InitLogging(/* argv */ nullptr);  
  bool parsed = ParsedOptions::Parse(raw_options, ignore_unrecognized, runtime_options);
  if (!parsed) {
    LOG(ERROR) << "Failed to parse options";
    return false;
  }
  return true;
}

4.4 ParsedOptions::Parse

位于/art/runtime/parsed_options.cc

bool ParsedOptions::Parse(const RuntimeOptions& options,
                          bool ignore_unrecognized,
                          RuntimeArgumentMap* runtime_options) {
  CHECK(runtime_options != nullptr);
  ParsedOptions parser;
  return parser.DoParse(options, ignore_unrecognized, runtime_options);
}

4.5 ParsedOptions::DoParse

bool ParsedOptions::DoParse(const RuntimeOptions& options,
                            bool ignore_unrecognized,
                            RuntimeArgumentMap* runtime_options) {
  ...
  //将字符串参数生成对应的M::key结构
  auto parser = MakeParser(ignore_unrecognized);

  std::vector<std::string> argv_list;
  //解析带有extraOption的参数, [5.2]
  if (!ProcessSpecialOptions(options, nullptr, &argv_list)) {
    return false;
  }

  CmdlineResult parse_result = parser->Parse(argv_list);

  // 处理parse errors
  if (parse_result.IsError()) {
    ...
  }

  using M = RuntimeArgumentMap;
  RuntimeArgumentMap args = parser->ReleaseArgumentsMap();

  if (args.Exists(M::Help)) {
    //-help
    Usage(nullptr);
    return false;
  } else if (args.Exists(M::ShowVersion)) {
    //-showversion
    UsageMessage(stdout, "ART version %s\n", Runtime::GetVersion());
    Exit(0);
  } else if (args.Exists(M::BootClassPath)) {
    //-Xbootclasspath
    LOG(INFO) << "setting boot class path to " << *args.Get(M::BootClassPath);
  }

  //-Xusejit和-Xint不能同时使用
  if (args.GetOrDefault(M::UseJitCompilation) && args.GetOrDefault(M::Interpret)) {
    Usage("-Xusejit:true and -Xint cannot be specified together");
    Exit(0);
  }

  //获取默认的bootclasspath
  if (getenv("BOOTCLASSPATH") != nullptr) {
    args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));
  }
  //获取默认的classpath
  if (getenv("CLASSPATH") != nullptr) {
    args.SetIfMissing(M::ClassPath, std::string(getenv("CLASSPATH")));
  }
  //设置参数-XX:ParallelGCThreads=0u
  //kDefaultEnableParallelGC=false
  args.SetIfMissing(M::ParallelGCThreads, gc::Heap::kDefaultEnableParallelGC ?
      static_cast<unsigned int>(sysconf(_SC_NPROCESSORS_CONF) - 1u) : 0u);

  // -verbose:gc
  {
    LogVerbosity *log_verbosity = args.Get(M::Verbose);
    if (log_verbosity != nullptr) {
      gLogVerbosity = *log_verbosity;
    }
  }
  MaybeOverrideVerbosity();

  // 设置-Xprofile:,由于在启动时没有指定这个参数,所以使用默认值:
  Trace::SetDefaultClockSource(args.GetOrDefault(M::ProfileClock));
  //再次检查extraInfo
  if (!ProcessSpecialOptions(options, &args, nullptr)) {
      return false;
  }

  {

    // 如果没有设置,background回收器是默认的homogeneous compaction
    // 如果foreground回收器是GSS, 则background也是GSS
    // 如果是low memory模式, 则使用semispace
    gc::CollectorType background_collector_type_;
    gc::CollectorType collector_type_ = (XGcOption{}).collector_type_;  // NOLINT [whitespace/braces] [5]
    //查看是否存在-XX:LowMemoryMode,实际上是没有的
    bool low_memory_mode_ = args.Exists(M::LowMemoryMode);
    //获取参数-XX:BackgroundGC,实际上并未指定,所以使用默认值
    background_collector_type_ = args.GetOrDefault(M::BackgroundGc);
    {
      //获取参数-Xgc,实际上参数中并没有指定
      ...
    }

    if (background_collector_type_ == gc::kCollectorTypeNone) {
      if (collector_type_ != gc::kCollectorTypeGSS) {
        //如果foreground回收器不是GSS,且当前是low_memory_mode,则使用SS, 否则指定为homogeneous compact
        background_collector_type_ = low_memory_mode_ ?
            gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
      } else {
        background_collector_type_ = collector_type_;
      }
    }

    args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });
  }

#if defined(ART_TARGET)
  std::string core_jar("/core.jar");
  std::string core_libart_jar("/core-libart.jar");
#else
  // The host uses hostdex files.
  std::string core_jar("/core-hostdex.jar");
  std::string core_libart_jar("/core-libart-hostdex.jar");
#endif
  //获取-Xbootclasspath,之前已经通过getenv("BOOTCLASSPATH")设置
  auto boot_class_path_string = args.GetOrDefault(M::BootClassPath);
  //在bootclasspath中查找/core.jar, 如果找到的话,则用/core-libart.jar替换
  size_t core_jar_pos = boot_class_path_string.find(core_jar);
  if (core_jar_pos != std::string::npos) {
    boot_class_path_string.replace(core_jar_pos, core_jar.size(), core_libart_jar);
    args.Set(M::BootClassPath, boot_class_path_string);
  }

  {
    //获取-Xbootclasspath和-Xbootclasspath-location:
    auto&& boot_class_path = args.GetOrDefault(M::BootClassPath);
    auto&& boot_class_path_locations = args.GetOrDefault(M::BootClassPathLocations);
    //实际并未指定-Xbootclasspath-location
    if (args.Exists(M::BootClassPathLocations)) {
      //如果boot_class_path_locations的path数量不等于boot_class_path中的path路径,打印Usage并返回
      ...
    }
  }
  //如果没有指定compilercallbacks回调函数(实际参数中并没有指定)以及没有指定-Ximage(实际参数也并未指定)
  //指定image文件路径是/system/framework/boot.art
  if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {
    std::string image = GetAndroidRoot();
    image += "/framework/boot.art";
    args.Set(M::Image, image);
  }

  //获取-XX:HeapGrowthLimit(实际参数中为256m),判断是否小于0或者大于-Xmx(实际为512m)
  //如果小于0或者大于-Xmx,则将-XX:HeapGrowthLimit设为-Xmx的值
  if (args.GetOrDefault(M::HeapGrowthLimit) <= 0u ||
      args.GetOrDefault(M::HeapGrowthLimit) > args.GetOrDefault(M::MemoryMaximumSize)) {
    args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
  }

  //实际参数中并没有指定-Xexperimental
  if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kLambdas) {
    ...
  }

  *runtime_options = std::move(args);
  return true;
}

DoParse方法主要做了以下几件事:

  1. 先将所有的字符串参数名称集中生成对应的RuntimeArgumentMap::[KEY]结构
  2. 从实际传入的参数中解析extra_info,extra_info用来hook函数,实际传入的参数中hook的是下面三个函数:exit,vfprintf,sensitiveThread
  3. 判断传入的参数中是否有-help,-showversion,如果存在,则打印对应的帮助信息并返回
  4. 确保-Xusejit-Xint不能共同使用
  5. getenv(BOOTCLASSPATH)并设置-Xbootclasspath
  6. getenv(CLASSPATH)并设置-Xclasspath
  7. 设置-XX:ParallelGCThreads=0u
  8. 根据实际传入的参数设置LogVerbosity
  9. 获取Xprofile,由于实际参数中没有设置,所以使用默认值,并设置Trace::SetDefaultClockSource
  10. 再次检索extra_info
  11. 根据kuseReadBarrier的值确定foreground GC, 如果kuseReadBarrier=true,foregroundGC=CC,否则foregroundGC=default;查看实际参数中是否指定-Xgc,如果指定了则将参数指定的GC设为background GC(实际参数中并未指定);如果foreground GC是GSS,则background GC也是GSS;如果foreground GC不是GSS,则如果实际参数中指定了-XX:LowMemoryMode(实际参数中并未指定), 即低内存模式下backgroundGC=GSS, 否则backgroundGC=homogeneous Space Compact
  12. 如果bootclasspath中包含/core.jar,则用/core-libart.jar替换
  13. 如果指定了-Xbootclasspath-location,检查-Xbootclasspath-location锁指定的path数量是否和-Xbootclasspath中的path数量一样,不一样输出Usage并返回
  14. 由于实际参数中并没有指定-Ximage:和名为compilercallbacksextra_info,指定Runtime的Image文件是/system/framework/boot.art
  15. 确保-XX:HeapGrowthLimit大于0同时小于-Xmx

4.5.1 ParsedOptions::ProcessSpecialOptions

bool ParsedOptions::ProcessSpecialOptions(const RuntimeOptions& options,
                                          RuntimeArgumentMap* runtime_options,
                                          std::vector<std::string>* out_options) {
  using M = RuntimeArgumentMap;

  for (size_t i = 0; i < options.size(); ++i) {
      const std::string option(options[i].first);
      if (option == "bootclasspath") {
        ... //实际并没有设置
      } else if (option == "compilercallbacks") {
        ... //实际并没有设置
      } else if (option == "imageinstructionset") {
        ... //实际并没有设置
      } else if (option == "sensitiveThread") {
        const void* hook = options[i].second;
        bool (*hook_is_sensitive_thread)() = reinterpret_cast<bool (*)()>(const_cast<void*>(hook));

        if (runtime_options != nullptr) {
          runtime_options->Set(M::HookIsSensitiveThread, hook_is_sensitive_thread);
        }
      } else if (option == "vfprintf") {
        const void* hook = options[i].second;
        if (hook == nullptr) {
          Usage("vfprintf argument was nullptr");
          return false;
        }
        int (*hook_vfprintf)(FILE *, const char*, va_list) =
            reinterpret_cast<int (*)(FILE *, const char*, va_list)>(const_cast<void*>(hook));

        if (runtime_options != nullptr) {
          runtime_options->Set(M::HookVfprintf, hook_vfprintf);
        }
        hook_vfprintf_ = hook_vfprintf;
      } else if (option == "exit") {
        const void* hook = options[i].second;
        if (hook == nullptr) {
          Usage("exit argument was nullptr");
          return false;
        }
        void(*hook_exit)(jint) = reinterpret_cast<void(*)(jint)>(const_cast<void*>(hook));
        if (runtime_options != nullptr) {
          runtime_options->Set(M::HookExit, hook_exit);
        }
        hook_exit_ = hook_exit;
      } else if (option == "abort") {
        ... //实际并没有设置
      } else {
        ... //实际并没有设置
      }
  }

  return true;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容