From 936e003c9545a5bf296f88f4ca630394b0c03d90 Mon Sep 17 00:00:00 2001 From: John Spray Date: Tue, 17 Mar 2015 18:38:41 +0000 Subject: [PATCH] mds: implement 'dump cache' asok command Retains old text format output when user specifies a filename, else returns JSON. JSON output will cope less well with large caches, but will be more useful for automated tests. Signed-off-by: John Spray --- src/mds/MDCache.cc | 123 +++++++++++++++++++++++++++++++++------------ src/mds/MDCache.h | 8 ++- src/mds/MDS.cc | 15 ++++-- 3 files changed, 111 insertions(+), 35 deletions(-) diff --git a/src/mds/MDCache.cc b/src/mds/MDCache.cc index 81ea780e64b5..915e700fbd0e 100644 --- a/src/mds/MDCache.cc +++ b/src/mds/MDCache.cc @@ -11807,63 +11807,124 @@ void MDCache::show_cache() } } +void MDCache::dump_cache(std::string const &file_name) +{ + dump_cache(file_name.c_str(), NULL); +} -void MDCache::dump_cache(const char *fn) +void MDCache::dump_cache(Formatter *f) { - int r; - char deffn[200]; - if (!fn) { - snprintf(deffn, sizeof(deffn), "cachedump.%d.mds%d", (int)mds->mdsmap->get_epoch(), int(mds->get_nodeid())); - fn = deffn; - } + dump_cache(NULL, f); +} - dout(1) << "dump_cache to " << fn << dendl; +/** + * Dump the metadata cache, either to a Formatter, if + * provided, else to a plain text file. + */ +void MDCache::dump_cache(const char *fn, Formatter *f) +{ + int r = 0; + int fd = -1; - int fd = ::open(fn, O_WRONLY|O_CREAT|O_EXCL, 0600); - if (fd < 0) { - derr << "failed to open " << fn << ": " << cpp_strerror(errno) << dendl; - return; + if (f) { + f->open_array_section("inodes"); + } else { + char deffn[200]; + if (!fn) { + snprintf(deffn, sizeof(deffn), "cachedump.%d.mds%d", (int)mds->mdsmap->get_epoch(), int(mds->get_nodeid())); + fn = deffn; + } + + dout(1) << "dump_cache to " << fn << dendl; + + fd = ::open(fn, O_WRONLY|O_CREAT|O_EXCL, 0600); + if (fd < 0) { + derr << "failed to open " << fn << ": " << cpp_strerror(errno) << dendl; + return; + } } for (ceph::unordered_map::iterator it = inode_map.begin(); it != inode_map.end(); ++it) { CInode *in = it->second; - ostringstream ss; - ss << *in << std::endl; - std::string s = ss.str(); - r = safe_write(fd, s.c_str(), s.length()); - if (r < 0) - goto out; + if (f) { + f->open_object_section("inode"); + in->dump(f); + } else { + ostringstream ss; + ss << *in << std::endl; + std::string s = ss.str(); + r = safe_write(fd, s.c_str(), s.length()); + if (r < 0) { + goto out; + } + } list dfs; in->get_dirfrags(dfs); + if (f) { + f->open_array_section("dirfrags"); + } for (list::iterator p = dfs.begin(); p != dfs.end(); ++p) { CDir *dir = *p; - ostringstream tt; - tt << " " << *dir << std::endl; - string t = tt.str(); - r = safe_write(fd, t.c_str(), t.length()); - if (r < 0) - goto out; + if (f) { + f->open_object_section("dir"); + dir->dump(f); + } else { + ostringstream tt; + tt << " " << *dir << std::endl; + string t = tt.str(); + r = safe_write(fd, t.c_str(), t.length()); + if (r < 0) { + goto out; + } + } + if (f) { + f->open_array_section("dentries"); + } for (CDir::map_t::iterator q = dir->items.begin(); q != dir->items.end(); ++q) { CDentry *dn = q->second; - ostringstream uu; - uu << " " << *dn << std::endl; - string u = uu.str(); - r = safe_write(fd, u.c_str(), u.length()); - if (r < 0) - goto out; + if (f) { + f->open_object_section("dentry"); + dn->dump(f); + f->close_section(); + } else { + ostringstream uu; + uu << " " << *dn << std::endl; + string u = uu.str(); + r = safe_write(fd, u.c_str(), u.length()); + if (r < 0) { + goto out; + } + } + } + if (f) { + f->close_section(); //dentries } dir->check_rstats(); + if (f) { + f->close_section(); //dir + } + } + if (f) { + f->close_section(); // dirfrags + } + + if (f) { + f->close_section(); // inode } } out: - ::close(fd); + if (f) { + f->close_section(); // inodes + } else { + ::close(fd); + } } diff --git a/src/mds/MDCache.h b/src/mds/MDCache.h index dca96b958757..39b1f66f143e 100644 --- a/src/mds/MDCache.h +++ b/src/mds/MDCache.h @@ -1081,10 +1081,16 @@ public: void discard_delayed_expire(CDir *dir); +protected: + void dump_cache(const char *fn, Formatter *f); +public: + void dump_cache() {dump_cache(NULL, NULL);} + void dump_cache(const std::string &filename); + void dump_cache(Formatter *f); + // == crap fns == public: void show_cache(); - void dump_cache(const char *fn=0); void show_subtrees(int dbl=10); CInode *hack_pick_random_inode() { diff --git a/src/mds/MDS.cc b/src/mds/MDS.cc index 77727ae93562..2b785c607a41 100644 --- a/src/mds/MDS.cc +++ b/src/mds/MDS.cc @@ -347,6 +347,13 @@ bool MDS::asok_command(string command, cmdmap_t& cmdmap, string format, return true; } command_export_dir(f, path, (mds_rank_t)rank); + } else if (command == "dump cache") { + string path; + if(!cmd_getval(g_ceph_context, cmdmap, "path", path)) { + mdcache->dump_cache(f); + } else { + mdcache->dump_cache(path); + } } else if (command == "force_readonly") { mds_lock.Lock(); mdcache->force_readonly(); @@ -602,6 +609,11 @@ void MDS::set_up_admin_socket() asok_hook, "migrate a subtree to named MDS"); assert(r == 0); + r = admin_socket->register_command("dump cache", + "dump cache name=path,type=CephString,req=false", + asok_hook, + "dump metadata cache (optionally to a file)"); + assert(r == 0); r = admin_socket->register_command("session evict", "session evict name=client_id,type=CephString", asok_hook, @@ -1225,9 +1237,6 @@ COMMAND("heap " \ "mds", "*", "cli,rest") }; -// FIXME: reinstate dumpcache as an admin socket command -// -- it makes no sense for it to be a remote command when -// the output is a local file // FIXME: reinstate issue_caps, try_eval, fragment_dir, merge_dir // *if* it makes sense to do so (or should these be admin socket things?) -- 2.47.3